blob: 52931af4ab572171d84414386a5db3452cdde53e [file] [log] [blame]
// 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/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/permission_manager.h"
#include "content/public/browser/permission_type.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/lib/headless_macros.h"
#include "headless/public/devtools/domains/inspector.h"
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/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 "headless/test/test_protocol_handler.h"
#include "headless/test/test_url_request_job.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/cookie_store.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/url_request/url_request_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/gfx/geometry/size.h"
using testing::UnorderedElementsAre;
namespace headless {
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateAndDestroyBrowserContext) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
browser_context->Close();
EXPECT_TRUE(browser()->GetAllBrowserContexts().empty());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest,
CreateAndDoNotDestroyBrowserContext) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
// We check that HeadlessBrowser correctly handles non-closed BrowserContexts.
// We can rely on Chromium DCHECKs to capture this.
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateAndDestroyWebContents) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
EXPECT_TRUE(web_contents);
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
EXPECT_THAT(browser_context->GetAllWebContents(),
UnorderedElementsAre(web_contents));
// TODO(skyostil): Verify viewport dimensions once we can.
web_contents->Close();
EXPECT_TRUE(browser_context->GetAllWebContents().empty());
browser_context->Close();
EXPECT_TRUE(browser()->GetAllBrowserContexts().empty());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest,
WebContentsAreDestroyedWithContext) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
EXPECT_TRUE(web_contents);
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
EXPECT_THAT(browser_context->GetAllWebContents(),
UnorderedElementsAre(web_contents));
browser_context->Close();
EXPECT_TRUE(browser()->GetAllBrowserContexts().empty());
// If WebContents are not destroyed, Chromium DCHECKs will capture this.
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateAndDoNotDestroyWebContents) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
EXPECT_TRUE(web_contents);
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
EXPECT_THAT(browser_context->GetAllWebContents(),
UnorderedElementsAre(web_contents));
// If WebContents are not destroyed, Chromium DCHECKs will capture this.
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, DestroyAndCreateTwoWebContents) {
HeadlessBrowserContext* browser_context1 =
browser()->CreateBrowserContextBuilder().Build();
EXPECT_TRUE(browser_context1);
HeadlessWebContents* web_contents1 =
browser_context1->CreateWebContentsBuilder().Build();
EXPECT_TRUE(web_contents1);
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context1));
EXPECT_THAT(browser_context1->GetAllWebContents(),
UnorderedElementsAre(web_contents1));
HeadlessBrowserContext* browser_context2 =
browser()->CreateBrowserContextBuilder().Build();
EXPECT_TRUE(browser_context2);
HeadlessWebContents* web_contents2 =
browser_context2->CreateWebContentsBuilder().Build();
EXPECT_TRUE(web_contents2);
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context1, browser_context2));
EXPECT_THAT(browser_context1->GetAllWebContents(),
UnorderedElementsAre(web_contents1));
EXPECT_THAT(browser_context2->GetAllWebContents(),
UnorderedElementsAre(web_contents2));
browser_context1->Close();
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context2));
EXPECT_THAT(browser_context2->GetAllWebContents(),
UnorderedElementsAre(web_contents2));
browser_context2->Close();
EXPECT_TRUE(browser()->GetAllBrowserContexts().empty());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateWithBadURL) {
GURL bad_url("not_valid");
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(bad_url)
.Build();
EXPECT_FALSE(web_contents);
EXPECT_TRUE(browser_context->GetAllWebContents().empty());
}
class HeadlessBrowserTestWithProxy : public HeadlessBrowserTest {
public:
HeadlessBrowserTestWithProxy()
: proxy_server_(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL("headless/test/data"))) {
}
void SetUp() override {
ASSERT_TRUE(proxy_server_.Start());
HeadlessBrowserTest::SetUp();
}
void TearDown() override {
proxy_server_.Stop();
HeadlessBrowserTest::TearDown();
}
net::SpawnedTestServer* proxy_server() { return &proxy_server_; }
private:
net::SpawnedTestServer proxy_server_;
};
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTestWithProxy, SetProxyServer) {
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetProxyServer(proxy_server()->host_port_pair())
.Build();
// Load a page which doesn't actually exist, but for which the our proxy
// returns valid content anyway.
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
EXPECT_THAT(browser()->GetAllBrowserContexts(),
UnorderedElementsAre(browser_context));
EXPECT_THAT(browser_context->GetAllWebContents(),
UnorderedElementsAre(web_contents));
web_contents->Close();
EXPECT_TRUE(browser_context->GetAllWebContents().empty());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, SetHostResolverRules) {
EXPECT_TRUE(embedded_test_server()->Start());
std::string host_resolver_rules =
base::StringPrintf("MAP not-an-actual-domain.tld 127.0.0.1:%d",
embedded_test_server()->host_port_pair().port());
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetHostResolverRules(host_resolver_rules)
.Build();
// Load a page which doesn't actually exist, but which is turned into a valid
// address by our host resolver rules.
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
.Build();
EXPECT_TRUE(web_contents);
EXPECT_TRUE(WaitForLoad(web_contents));
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpProtocolHandler) {
const std::string kResponseBody = "<p>HTTP response body</p>";
ProtocolHandlerMap protocol_handlers;
protocol_handlers[url::kHttpScheme] =
base::MakeUnique<TestProtocolHandler>(kResponseBody);
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetProtocolHandlers(std::move(protocol_handlers))
.Build();
// Load a page which doesn't actually exist, but which is fetched by our
// custom protocol handler.
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
.Build();
EXPECT_TRUE(web_contents);
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);
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpsProtocolHandler) {
const std::string kResponseBody = "<p>HTTPS response body</p>";
ProtocolHandlerMap protocol_handlers;
protocol_handlers[url::kHttpsScheme] =
base::MakeUnique<TestProtocolHandler>(kResponseBody);
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetProtocolHandlers(std::move(protocol_handlers))
.Build();
// Load a page which doesn't actually exist, but which is fetched by our
// custom protocol handler.
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("https://not-an-actual-domain.tld/hello.html"))
.Build();
EXPECT_TRUE(web_contents);
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);
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, WebGLSupported) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
bool webgl_supported;
EXPECT_TRUE(
EvaluateScript(web_contents,
"(document.createElement('canvas').getContext('webgl')"
" instanceof WebGLRenderingContext)")
->GetResult()
->GetValue()
->GetAsBoolean(&webgl_supported));
EXPECT_TRUE(webgl_supported);
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ClipboardCopyPasteText) {
// Tests copy-pasting text with the clipboard in headless mode.
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ASSERT_TRUE(clipboard);
base::string16 paste_text = base::ASCIIToUTF16("Clippy!");
for (ui::ClipboardType type :
{ui::CLIPBOARD_TYPE_COPY_PASTE, ui::CLIPBOARD_TYPE_SELECTION,
ui::CLIPBOARD_TYPE_DRAG}) {
if (!ui::Clipboard::IsSupportedClipboardType(type))
continue;
{
ui::ScopedClipboardWriter writer(type);
writer.WriteText(paste_text);
}
base::string16 copy_text;
clipboard->ReadText(type, &copy_text);
EXPECT_EQ(paste_text, copy_text);
}
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, DefaultSizes) {
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
HeadlessBrowser::Options::Builder builder;
const HeadlessBrowser::Options kDefaultOptions = builder.Build();
int screen_width;
int screen_height;
int window_width;
int window_height;
EXPECT_TRUE(EvaluateScript(web_contents, "screen.width")
->GetResult()
->GetValue()
->GetAsInteger(&screen_width));
EXPECT_TRUE(EvaluateScript(web_contents, "screen.height")
->GetResult()
->GetValue()
->GetAsInteger(&screen_height));
EXPECT_TRUE(EvaluateScript(web_contents, "window.innerWidth")
->GetResult()
->GetValue()
->GetAsInteger(&window_width));
EXPECT_TRUE(EvaluateScript(web_contents, "window.innerHeight")
->GetResult()
->GetValue()
->GetAsInteger(&window_height));
#if !defined(OS_MACOSX)
// On Mac headless does not override the screen dimensions, so they are
// left with the actual screen values.
EXPECT_EQ(kDefaultOptions.window_size.width(), screen_width);
EXPECT_EQ(kDefaultOptions.window_size.height(), screen_height);
#endif // !defined(OS_MACOSX)
EXPECT_EQ(kDefaultOptions.window_size.width(), window_width);
EXPECT_EQ(kDefaultOptions.window_size.height(), window_height);
}
namespace {
// True if the request method is "safe" (per section 4.2.1 of RFC 7231).
bool IsMethodSafe(const std::string& method) {
return method == "GET" || method == "HEAD" || method == "OPTIONS" ||
method == "TRACE";
}
class ProtocolHandlerWithCookies
: public net::URLRequestJobFactory::ProtocolHandler {
public:
ProtocolHandlerWithCookies(net::CookieList* sent_cookies);
~ProtocolHandlerWithCookies() override {}
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override;
private:
net::CookieList* sent_cookies_; // Not owned.
DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerWithCookies);
};
class URLRequestJobWithCookies : public TestURLRequestJob {
public:
URLRequestJobWithCookies(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
net::CookieList* sent_cookies);
~URLRequestJobWithCookies() override {}
// net::URLRequestJob implementation:
void Start() override;
private:
void SaveCookiesAndStart(const net::CookieList& cookie_list);
net::CookieList* sent_cookies_; // Not owned.
base::WeakPtrFactory<URLRequestJobWithCookies> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(URLRequestJobWithCookies);
};
ProtocolHandlerWithCookies::ProtocolHandlerWithCookies(
net::CookieList* sent_cookies)
: sent_cookies_(sent_cookies) {}
net::URLRequestJob* ProtocolHandlerWithCookies::MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
return new URLRequestJobWithCookies(request, network_delegate, sent_cookies_);
}
URLRequestJobWithCookies::URLRequestJobWithCookies(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
net::CookieList* sent_cookies)
// Return an empty response for every request.
: TestURLRequestJob(request, network_delegate, ""),
sent_cookies_(sent_cookies),
weak_factory_(this) {}
void URLRequestJobWithCookies::Start() {
net::CookieStore* cookie_store = request_->context()->cookie_store();
net::CookieOptions options;
options.set_include_httponly();
// See net::URLRequestHttpJob::AddCookieHeaderAndStart().
url::Origin requested_origin(request_->url());
url::Origin site_for_cookies(request_->first_party_for_cookies());
if (net::registry_controlled_domains::SameDomainOrHost(
requested_origin, site_for_cookies,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
if (net::registry_controlled_domains::SameDomainOrHost(
requested_origin, request_->initiator(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
options.set_same_site_cookie_mode(
net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
} else if (IsMethodSafe(request_->method())) {
options.set_same_site_cookie_mode(
net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
}
}
cookie_store->GetCookieListWithOptionsAsync(
request_->url(), options,
base::Bind(&URLRequestJobWithCookies::SaveCookiesAndStart,
weak_factory_.GetWeakPtr()));
}
void URLRequestJobWithCookies::SaveCookiesAndStart(
const net::CookieList& cookie_list) {
*sent_cookies_ = cookie_list;
NotifyHeadersComplete();
}
} // namespace
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ReadCookiesInProtocolHandler) {
net::CookieList sent_cookies;
ProtocolHandlerMap protocol_handlers;
protocol_handlers[url::kHttpsScheme] =
base::MakeUnique<ProtocolHandlerWithCookies>(&sent_cookies);
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetProtocolHandlers(std::move(protocol_handlers))
.Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("https://example.com/cookie.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
// The first load has no cookies.
EXPECT_EQ(0u, sent_cookies.size());
// Set a cookie and reload the page.
EXPECT_FALSE(EvaluateScript(
web_contents,
"document.cookie = 'shape=oblong', window.location.reload()")
->HasExceptionDetails());
EXPECT_TRUE(WaitForLoad(web_contents));
// We should have sent the cookie this time.
EXPECT_EQ(1u, sent_cookies.size());
EXPECT_EQ("shape", sent_cookies[0].Name());
EXPECT_EQ("oblong", sent_cookies[0].Value());
}
namespace {
class CookieSetter {
public:
CookieSetter(HeadlessBrowserTest* browser_test,
HeadlessWebContents* web_contents,
std::unique_ptr<network::SetCookieParams> set_cookie_params)
: browser_test_(browser_test),
web_contents_(web_contents),
devtools_client_(HeadlessDevToolsClient::Create()) {
web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
devtools_client_->GetNetwork()->GetExperimental()->SetCookie(
std::move(set_cookie_params),
base::Bind(&CookieSetter::OnSetCookieResult, base::Unretained(this)));
}
~CookieSetter() {
web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
}
void OnSetCookieResult(std::unique_ptr<network::SetCookieResult> result) {
result_ = std::move(result);
browser_test_->FinishAsynchronousTest();
}
std::unique_ptr<network::SetCookieResult> TakeResult() {
return std::move(result_);
}
private:
HeadlessBrowserTest* browser_test_; // Not owned.
HeadlessWebContents* web_contents_; // Not owned.
std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
std::unique_ptr<network::SetCookieResult> result_;
DISALLOW_COPY_AND_ASSIGN(CookieSetter);
};
} // namespace
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, SetCookiesWithDevTools) {
net::CookieList sent_cookies;
ProtocolHandlerMap protocol_handlers;
protocol_handlers[url::kHttpsScheme] =
base::WrapUnique(new ProtocolHandlerWithCookies(&sent_cookies));
HeadlessBrowserContext* browser_context =
browser()
->CreateBrowserContextBuilder()
.SetProtocolHandlers(std::move(protocol_handlers))
.Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(GURL("https://example.com/cookie.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
// The first load has no cookies.
EXPECT_EQ(0u, sent_cookies.size());
// Set some cookies.
{
std::unique_ptr<network::SetCookieParams> set_cookie_params =
network::SetCookieParams::Builder()
.SetUrl("https://example.com")
.SetName("shape")
.SetValue("oblong")
.Build();
CookieSetter cookie_setter(this, web_contents,
std::move(set_cookie_params));
RunAsynchronousTest();
std::unique_ptr<network::SetCookieResult> result =
cookie_setter.TakeResult();
EXPECT_TRUE(result->GetSuccess());
}
{
// Try setting all the fields so we notice if the protocol for any of them
// changes.
std::unique_ptr<network::SetCookieParams> set_cookie_params =
network::SetCookieParams::Builder()
.SetUrl("https://other.com")
.SetName("shape")
.SetValue("trapezoid")
.SetDomain("other.com")
.SetPath("")
.SetSecure(true)
.SetHttpOnly(true)
.SetSameSite(network::CookieSameSite::EXACT)
.SetExpirationDate(0)
.Build();
CookieSetter cookie_setter(this, web_contents,
std::move(set_cookie_params));
RunAsynchronousTest();
std::unique_ptr<network::SetCookieResult> result =
cookie_setter.TakeResult();
EXPECT_TRUE(result->GetSuccess());
}
// Reload the page.
EXPECT_FALSE(EvaluateScript(web_contents, "window.location.reload();")
->HasExceptionDetails());
EXPECT_TRUE(WaitForLoad(web_contents));
// We should have sent the matching cookies this time.
EXPECT_EQ(1u, sent_cookies.size());
EXPECT_EQ("shape", sent_cookies[0].Name());
EXPECT_EQ("oblong", sent_cookies[0].Value());
}
// TODO(skyostil): This test currently relies on being able to run a shell
// script.
#if defined(OS_POSIX)
#define MAYBE_RendererCommandPrefixTest RendererCommandPrefixTest
#else
#define MAYBE_RendererCommandPrefixTest DISABLED_RendererCommandPrefixTest
#endif // defined(OS_POSIX)
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, MAYBE_RendererCommandPrefixTest) {
base::ThreadRestrictions::SetIOAllowed(true);
base::FilePath launcher_stamp;
base::CreateTemporaryFile(&launcher_stamp);
base::FilePath launcher_script;
FILE* launcher_file = base::CreateAndOpenTemporaryFile(&launcher_script);
fprintf(launcher_file, "#!/bin/sh\n");
fprintf(launcher_file, "echo $@ > %s\n", launcher_stamp.value().c_str());
fprintf(launcher_file, "exec $@\n");
fclose(launcher_file);
base::SetPosixFilePermissions(launcher_script,
base::FILE_PERMISSION_READ_BY_USER |
base::FILE_PERMISSION_EXECUTE_BY_USER);
base::CommandLine::ForCurrentProcess()->AppendSwitch("--no-sandbox");
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
"--renderer-cmd-prefix", launcher_script.value());
EXPECT_TRUE(embedded_test_server()->Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
// Make sure the launcher was invoked when starting the renderer.
std::string stamp;
EXPECT_TRUE(base::ReadFileToString(launcher_stamp, &stamp));
EXPECT_GE(stamp.find("--type=renderer"), 0u);
base::DeleteFile(launcher_script, false);
base::DeleteFile(launcher_stamp, false);
}
class CrashReporterTest : public HeadlessBrowserTest,
public HeadlessWebContents::Observer,
inspector::ExperimentalObserver {
public:
CrashReporterTest() : devtools_client_(HeadlessDevToolsClient::Create()) {}
~CrashReporterTest() override {}
void SetUp() override {
base::ThreadRestrictions::SetIOAllowed(true);
base::CreateNewTempDirectory("CrashReporterTest", &crash_dumps_dir_);
EXPECT_FALSE(options()->enable_crash_reporter);
options()->enable_crash_reporter = true;
options()->crash_dumps_dir = crash_dumps_dir_;
HeadlessBrowserTest::SetUp();
}
void TearDown() override {
base::ThreadRestrictions::SetIOAllowed(true);
base::DeleteFile(crash_dumps_dir_, /* recursive */ false);
}
// HeadlessWebContents::Observer implementation:
void DevToolsTargetReady() override {
EXPECT_TRUE(web_contents_->GetDevToolsTarget());
web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
}
// inspector::ExperimentalObserver implementation:
void OnTargetCrashed(const inspector::TargetCrashedParams&) override {
FinishAsynchronousTest();
}
protected:
HeadlessBrowserContext* browser_context_ = nullptr;
HeadlessWebContents* web_contents_ = nullptr;
std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
base::FilePath crash_dumps_dir_;
};
// TODO(skyostil): Minidump generation currently is only supported on Linux.
#if defined(HEADLESS_USE_BREAKPAD)
#define MAYBE_GenerateMinidump GenerateMinidump
#else
#define MAYBE_GenerateMinidump DISABLED_GenerateMinidump
#endif // defined(HEADLESS_USE_BREAKPAD)
IN_PROC_BROWSER_TEST_F(CrashReporterTest, MAYBE_GenerateMinidump) {
// Navigates a tab to chrome://crash and checks that a minidump is generated.
// Note that we only test renderer crashes here -- browser crashes need to be
// tested with a separate harness.
//
// The case where crash reporting is disabled is covered by
// HeadlessCrashObserverTest.
browser_context_ = browser()->CreateBrowserContextBuilder().Build();
web_contents_ = browser_context_->CreateWebContentsBuilder()
.SetInitialURL(GURL(content::kChromeUICrashURL))
.Build();
web_contents_->AddObserver(this);
RunAsynchronousTest();
// The target has crashed and should no longer be there.
EXPECT_FALSE(web_contents_->GetDevToolsTarget());
// Check that one minidump got created.
{
base::ThreadRestrictions::SetIOAllowed(true);
base::FileEnumerator it(crash_dumps_dir_, /* recursive */ false,
base::FileEnumerator::FILES);
base::FilePath minidump = it.Next();
EXPECT_FALSE(minidump.empty());
EXPECT_EQ(".dmp", minidump.Extension());
EXPECT_TRUE(it.Next().empty());
}
web_contents_->RemoveObserver(this);
web_contents_->Close();
web_contents_ = nullptr;
browser_context_->Close();
browser_context_ = nullptr;
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, PermissionManagerAlwaysASK) {
GURL url("https://example.com");
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* headless_web_contents =
browser_context->CreateWebContentsBuilder().Build();
EXPECT_TRUE(headless_web_contents);
HeadlessWebContentsImpl* web_contents =
HeadlessWebContentsImpl::From(headless_web_contents);
content::PermissionManager* permission_manager =
web_contents->browser_context()->GetPermissionManager();
EXPECT_NE(nullptr, permission_manager);
// Check that the permission manager returns ASK for a given permission type.
EXPECT_EQ(blink::mojom::PermissionStatus::ASK,
permission_manager->GetPermissionStatus(
content::PermissionType::NOTIFICATIONS, url, url));
}
} // namespace headless