blob: 071cbe4b2f1e7794ecebb4c953cb1e8657171e30 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "cc/test/pixel_test_utils.h"
#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
#include "components/viz/common/switches.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "headless/app/headless_shell_switches.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/headless_browser.h"
#include "headless/public/headless_web_contents.h"
#include "headless/test/headless_browser_test.h"
#include "headless/test/headless_browser_test_utils.h"
#include "headless/test/headless_devtooled_browsertest.h"
#include "headless/test/test_network_interceptor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_f.h"
#include "url/gurl.h"
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Not;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;
using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
namespace headless {
class HeadlessWebContentsTest : public HeadlessBrowserTest {};
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) {
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));
EXPECT_THAT(browser_context->GetAllWebContents(),
UnorderedElementsAre(web_contents));
}
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, WindowOpen) {
EXPECT_TRUE(embedded_test_server()->Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(embedded_test_server()->GetURL("/window_open.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
HeadlessWebContentsImpl* child = nullptr;
HeadlessWebContentsImpl* parent = nullptr;
for (HeadlessWebContents* c : browser_context->GetAllWebContents()) {
HeadlessWebContentsImpl* impl = HeadlessWebContentsImpl::From(c);
if (impl->window_id() == 1)
parent = impl;
else if (impl->window_id() == 2)
child = impl;
}
EXPECT_NE(nullptr, parent);
EXPECT_NE(nullptr, child);
EXPECT_NE(parent, child);
// Mac doesn't have WindowTreeHosts.
if (parent && child && parent->window_tree_host())
EXPECT_NE(parent->window_tree_host(), child->window_tree_host());
gfx::Rect expected_bounds(0, 0, 200, 100);
#if !BUILDFLAG(IS_MAC)
EXPECT_EQ(expected_bounds, child->web_contents()->GetViewBounds());
EXPECT_EQ(expected_bounds, child->web_contents()->GetContainerBounds());
#else // !BUILDFLAG(IS_MAC)
// Mac does not support GetViewBounds() and view positions are random.
EXPECT_EQ(expected_bounds.size(),
child->web_contents()->GetContainerBounds().size());
#endif // !BUILDFLAG(IS_MAC)
}
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest,
FocusOfHeadlessWebContents_IsIndependent) {
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();
WaitForLoadAndGainFocus(web_contents);
EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
DictHasValue("result.result.value", true));
HeadlessWebContents* web_contents2 =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
.Build();
WaitForLoadAndGainFocus(web_contents2);
// Focus of different WebContents is independent.
EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
DictHasValue("result.result.value", true));
EXPECT_THAT(EvaluateScript(web_contents2, "document.hasFocus()"),
DictHasValue("result.result.value", true));
}
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, HandleSSLError) {
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
ASSERT_TRUE(https_server.Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(https_server.GetURL("/hello.html"))
.Build();
EXPECT_FALSE(WaitForLoad(web_contents));
}
namespace {
bool DecodePNG(const std::string& png_data, SkBitmap* bitmap) {
return gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(png_data.data()), png_data.size(),
bitmap);
}
} // namespace
// Parameter specifies whether --disable-gpu should be used.
class HeadlessWebContentsScreenshotTest
: public HeadlessDevTooledBrowserTest,
public ::testing::WithParamInterface<bool> {
public:
void SetUp() override {
EnablePixelOutput();
if (GetParam()) {
UseSoftwareCompositing();
SetUpWithoutGPU();
} else {
HeadlessDevTooledBrowserTest::SetUp();
}
}
void RunDevTooledTest() override {
devtools_client_.SendCommand(
"Runtime.evaluate",
Param("expression", "document.body.style.background = '#0000ff'"),
base::BindOnce(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted,
base::Unretained(this)));
}
void OnPageSetupCompleted(base::Value::Dict) {
devtools_client_.SendCommand(
"Page.captureScreenshot",
base::BindOnce(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured,
base::Unretained(this)));
}
void OnScreenshotCaptured(base::Value::Dict result) {
std::string png_data_base64 = DictString(result, "result.data");
ASSERT_FALSE(png_data_base64.empty());
std::string png_data;
ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
EXPECT_GT(png_data.size(), 0U);
SkBitmap result_bitmap;
EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
EXPECT_EQ(800, result_bitmap.width());
EXPECT_EQ(600, result_bitmap.height());
SkColor actual_color = result_bitmap.getColor(400, 300);
SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
EXPECT_EQ(expected_color, actual_color);
FinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest);
// Instantiate test case for both software and gpu compositing modes.
INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotTests,
HeadlessWebContentsScreenshotTest,
::testing::Bool());
// Regression test for crbug.com/832138.
class HeadlessWebContentsScreenshotWindowPositionTest
: public HeadlessWebContentsScreenshotTest {
public:
void RunDevTooledTest() override {
base::Value::Dict params;
params.Set("windowId",
HeadlessWebContentsImpl::From(web_contents_)->window_id());
params.SetByDottedPath("bounds.left", 600);
params.SetByDottedPath("bounds.top", 100);
params.SetByDottedPath("bounds.width", 800);
params.SetByDottedPath("bounds.height", 600);
browser_devtools_client_.SendCommand(
"Browser.setWindowBounds", std::move(params),
base::BindOnce(
&HeadlessWebContentsScreenshotWindowPositionTest::OnWindowBoundsSet,
base::Unretained(this)));
}
void OnWindowBoundsSet(base::Value::Dict result) {
EXPECT_NE(result.FindDict("result"), nullptr);
HeadlessWebContentsScreenshotTest::RunDevTooledTest();
}
};
#if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER)
// TODO(crbug.com/1086872): Disabled due to flakiness on Mac ASAN.
DISABLED_HEADLESS_DEVTOOLED_TEST_P(
HeadlessWebContentsScreenshotWindowPositionTest);
#else
HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotWindowPositionTest);
#endif
// Instantiate test case for both software and gpu compositing modes.
INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotWindowPositionTests,
HeadlessWebContentsScreenshotWindowPositionTest,
::testing::Bool());
// Regression test for https://crbug.com/733569.
class HeadlessWebContentsRequestStorageQuotaTest
: public HeadlessDevTooledBrowserTest {
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_.AddEventHandler(
"Runtime.consoleAPICalled",
base::BindRepeating(
&HeadlessWebContentsRequestStorageQuotaTest::OnConsoleAPICalled,
base::Unretained(this)));
SendCommandSync(devtools_client_, "Runtime.enable");
// Should not crash and call console.log() if quota request succeeds.
devtools_client_.SendCommand(
"Page.navigate",
Param("url", embedded_test_server()
->GetURL("/request_storage_quota.html")
.spec()));
}
void OnConsoleAPICalled(const base::Value::Dict& params) {
const base::Value::List* args = params.FindListByDottedPath("params.args");
ASSERT_NE(args, nullptr);
ASSERT_GT(args->size(), 0ul);
const base::Value* value = args->front().GetDict().Find("value");
ASSERT_NE(value, nullptr);
EXPECT_EQ(value->GetString(), "success");
FinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsRequestStorageQuotaTest);
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserTabChangeContent) {
EXPECT_TRUE(embedded_test_server()->Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
EXPECT_TRUE(WaitForLoad(web_contents));
std::string script = "window.location = '" +
embedded_test_server()->GetURL("/hello.html").spec() +
"';";
EXPECT_THAT(EvaluateScript(web_contents, script),
Not(DictHasKey("exceptionDetails")));
// This will time out if the previous script did not work.
EXPECT_TRUE(WaitForLoad(web_contents));
}
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserOpenInTab) {
EXPECT_TRUE(embedded_test_server()->Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder()
.SetInitialURL(embedded_test_server()->GetURL("/link.html"))
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
EXPECT_EQ(1u, browser_context->GetAllWebContents().size());
// Simulates a middle-button click on a link to ensure that the
// link is opened in a new tab by the browser and not by the renderer.
std::string script =
"var event = new MouseEvent('click', {'button': 1});"
"document.getElementsByTagName('a')[0].dispatchEvent(event);";
EXPECT_THAT(EvaluateScript(web_contents, script),
Not(DictHasKey("exceptionDetails")));
// Check that we have a new tab.
EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
}
// BeginFrameControl is not supported on MacOS.
#if !BUILDFLAG(IS_MAC)
// TODO(kvitekp): Check to see if this could be trimmed down by using
// Pre/PostRunAsynchronousTest().
class HeadlessWebContentsBeginFrameControlTest : public HeadlessBrowserTest {
public:
HeadlessWebContentsBeginFrameControlTest() {}
void SetUp() override {
EnablePixelOutput();
HeadlessBrowserTest::SetUp();
}
protected:
virtual std::string GetTestHtmlFile() = 0;
virtual void StartFrames() {}
virtual void OnFrameFinished(base::Value::Dict result) {}
void SetUpCommandLine(base::CommandLine* command_line) override {
HeadlessBrowserTest::SetUpCommandLine(command_line);
// See bit.ly/headless-rendering for why we use these flags.
command_line->AppendSwitch(::switches::kRunAllCompositorStagesBeforeDraw);
command_line->AppendSwitch(::switches::kDisableNewContentRenderingTimeout);
command_line->AppendSwitch(cc::switches::kDisableCheckerImaging);
command_line->AppendSwitch(cc::switches::kDisableThreadedAnimation);
command_line->AppendSwitch(blink::switches::kDisableThreadedScrolling);
}
void RunTest() {
browser_context_ = browser()->CreateBrowserContextBuilder().Build();
browser()->SetDefaultBrowserContext(browser_context_);
browser_devtools_client_.AttachToBrowser();
EXPECT_TRUE(embedded_test_server()->Start());
base::Value::Dict params;
params.Set("url", "about:blank");
params.Set("width", 200);
params.Set("height", 200);
params.Set("enableBeginFrameControl", true);
browser_devtools_client_.SendCommand(
"Target.createTarget", std::move(params),
base::BindOnce(
&HeadlessWebContentsBeginFrameControlTest::OnTargetCreated,
base::Unretained(this)));
RunAsynchronousTest();
browser_devtools_client_.DetachClient();
}
void OnTargetCreated(base::Value::Dict result) {
const std::string targetId = DictString(result, "result.targetId");
ASSERT_FALSE(targetId.empty());
web_contents_ = HeadlessWebContentsImpl::From(
browser()->GetWebContentsForDevToolsAgentHostId(targetId));
devtools_client_.AttachToWebContents(web_contents_->web_contents());
devtools_client_.SendCommand(
"Page.stopLoading",
base::BindOnce(
&HeadlessWebContentsBeginFrameControlTest::OnLoadingStopped,
base::Unretained(this)));
}
void OnLoadingStopped(base::Value::Dict) {
devtools_client_.AddEventHandler("Page.loadEventFired",
on_load_event_fired_handler_);
devtools_client_.SendCommand(
"Page.enable",
base::BindOnce(
&HeadlessWebContentsBeginFrameControlTest::OnPageDomainEnabled,
base::Unretained(this)));
}
void OnPageDomainEnabled(base::Value::Dict) {
devtools_client_.SendCommand(
"Page.navigate",
Param("url", embedded_test_server()->GetURL(GetTestHtmlFile()).spec()));
}
void OnLoadEventFired(const base::Value::Dict& params) {
TRACE_EVENT0("headless",
"HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired");
devtools_client_.SendCommand("Page.disable");
devtools_client_.RemoveEventHandler("Page.loadEventFired",
on_load_event_fired_handler_);
page_ready_ = true;
StartFrames();
}
void BeginFrame(bool screenshot) {
frame_in_flight_ = true;
num_begin_frames_++;
base::Value::Dict params;
if (screenshot)
params.Set("screenshot", base::Value::Dict());
devtools_client_.SendCommand(
"HeadlessExperimental.beginFrame", std::move(params),
base::BindOnce(&HeadlessWebContentsBeginFrameControlTest::FrameFinished,
base::Unretained(this)));
}
void FrameFinished(base::Value::Dict result) {
TRACE_EVENT2(
"headless", "HeadlessWebContentsBeginFrameControlTest::FrameFinished",
"has_damage", DictBool(result, "result.hasDamage"),
"has_screenshot_data", DictString(result, "result.screenshotData"));
// Post OnFrameFinished call so that any pending OnNeedsBeginFramesChanged
// call will be executed first.
browser()->BrowserMainThread()->PostTask(
FROM_HERE,
base::BindOnce(
&HeadlessWebContentsBeginFrameControlTest::NotifyOnFrameFinished,
base::Unretained(this), std::move(result)));
}
void NotifyOnFrameFinished(base::Value::Dict result) {
frame_in_flight_ = false;
OnFrameFinished(std::move(result));
}
void PostFinishAsynchronousTest() {
browser()->BrowserMainThread()->PostTask(
FROM_HERE,
base::BindOnce(
&HeadlessWebContentsBeginFrameControlTest::FinishAsynchronousTest,
base::Unretained(this)));
}
raw_ptr<HeadlessBrowserContext, DanglingUntriaged> browser_context_ =
nullptr; // Not owned.
raw_ptr<HeadlessWebContentsImpl, DanglingUntriaged> web_contents_ =
nullptr; // Not owned.
bool page_ready_ = false;
bool frame_in_flight_ = false;
int num_begin_frames_ = 0;
SimpleDevToolsProtocolClient browser_devtools_client_;
SimpleDevToolsProtocolClient devtools_client_;
SimpleDevToolsProtocolClient::EventCallback on_load_event_fired_handler_ =
base::BindRepeating(
&HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired,
base::Unretained(this));
};
class HeadlessWebContentsBeginFrameControlBasicTest
: public HeadlessWebContentsBeginFrameControlTest {
public:
HeadlessWebContentsBeginFrameControlBasicTest() = default;
protected:
std::string GetTestHtmlFile() override {
// Blue background.
return "/blue_page.html";
}
void StartFrames() override { BeginFrame(true); }
void OnFrameFinished(base::Value::Dict result) override {
if (num_begin_frames_ == 1) {
// First BeginFrame should have caused damage and have a screenshot.
EXPECT_TRUE(DictBool(result, "result.hasDamage"));
std::string png_data_base64 = DictString(result, "result.screenshotData");
ASSERT_FALSE(png_data_base64.empty());
std::string png_data;
ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
EXPECT_GT(png_data.size(), 0U);
SkBitmap result_bitmap;
EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
EXPECT_EQ(200, result_bitmap.width());
EXPECT_EQ(200, result_bitmap.height());
SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
SkColor actual_color = result_bitmap.getColor(100, 100);
EXPECT_EQ(expected_color, actual_color);
} else {
DCHECK_EQ(2, num_begin_frames_);
// Can't guarantee that the second BeginFrame didn't have damage, but it
// should not have a screenshot.
EXPECT_FALSE(result.FindStringByDottedPath("result.screenshotData"));
}
if (num_begin_frames_ < 2) {
// Don't capture a screenshot in the second BeginFrame.
BeginFrame(false);
} else {
// Post completion to avoid deleting the WebContents on the same callstack
// as frame finished callback.
PostFinishAsynchronousTest();
}
}
};
HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest);
class HeadlessWebContentsBeginFrameControlViewportTest
: public HeadlessWebContentsBeginFrameControlTest {
public:
HeadlessWebContentsBeginFrameControlViewportTest() = default;
protected:
std::string GetTestHtmlFile() override {
// Draws a 100x100px blue box at 200x200px.
return "/blue_box.html";
}
void StartFrames() override {
// Send a first BeginFrame to initialize the surface.
BeginFrame(false);
}
void SetUpViewport() {
base::Value::Dict params;
params.Set("width", 0);
params.Set("height", 0);
params.Set("deviceScaleFactor", 0);
params.Set("mobile", false);
params.SetByDottedPath("viewport.x", 200);
params.SetByDottedPath("viewport.y", 200);
params.SetByDottedPath("viewport.width", 100);
params.SetByDottedPath("viewport.height", 100);
params.SetByDottedPath("viewport.scale", 3);
devtools_client_.SendCommand(
"Emulation.setDeviceMetricsOverride", std::move(params),
base::BindOnce(&HeadlessWebContentsBeginFrameControlViewportTest::
OnSetDeviceMetricsOverrideDone,
base::Unretained(this)));
}
void OnSetDeviceMetricsOverrideDone(base::Value::Dict result) {
EXPECT_THAT(result, DictHasKey("result"));
// Take a screenshot in the second BeginFrame.
BeginFrame(true);
}
void OnFrameFinished(base::Value::Dict result) override {
if (num_begin_frames_ == 1) {
SetUpViewport();
return;
}
DCHECK_EQ(2, num_begin_frames_);
// Second BeginFrame should have a screenshot of the configured viewport and
// of the correct size.
EXPECT_TRUE(*result.FindBoolByDottedPath("result.hasDamage"));
std::string png_data_base64 = DictString(result, "result.screenshotData");
ASSERT_FALSE(png_data_base64.empty());
std::string png_data;
ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
ASSERT_GT(png_data.size(), 0ul);
SkBitmap result_bitmap;
EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
// Expect a 300x300 bitmap that is all blue.
SkBitmap expected_bitmap;
SkImageInfo info;
expected_bitmap.allocPixels(
SkImageInfo::MakeN32(300, 300, kOpaque_SkAlphaType), /*rowBytes=*/0);
expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
EXPECT_TRUE(
cc::MatchesBitmap(result_bitmap, expected_bitmap,
cc::ExactPixelComparator(/*discard_alpha=*/false)));
// Post completion to avoid deleting the WebContents on the same callstack
// as frame finished callback.
PostFinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlViewportTest);
#endif // !BUILDFLAG(IS_MAC)
class CookiesEnabled : public HeadlessDevTooledBrowserTest {
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_.AddEventHandler(
"Page.loadEventFired",
base::BindRepeating(&CookiesEnabled::OnLoadEventFired,
base::Unretained(this)));
devtools_client_.SendCommand("Page.enable");
devtools_client_.SendCommand(
"Page.navigate",
Param("url", embedded_test_server()->GetURL("/cookie.html").spec()));
}
void OnLoadEventFired(const base::Value::Dict& params) {
devtools_client_.SendCommand(
"Runtime.evaluate", Param("expression", "window.test_result"),
base::BindOnce(&CookiesEnabled::OnEvaluateResult,
base::Unretained(this)));
}
void OnEvaluateResult(base::Value::Dict result) {
EXPECT_EQ(DictString(result, "result.result.value"), "0");
FinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_F(CookiesEnabled);
namespace {
const char* kPageWhichOpensAWindow = R"(
<html>
<body>
<script>
const win = window.open('/page2.html');
if (!win)
console.error('ready');
win.addEventListener('load', () => console.log('ready'));
</script>
</body>
</html>
)";
const char* kPage2 = R"(
<html>
<body>
Page 2.
</body>
</html>
)";
} // namespace
class WebContentsOpenTest : public HeadlessDevTooledBrowserTest {
public:
void PreRunAsynchronousTest() override {
interceptor_ = std::make_unique<TestNetworkInterceptor>();
}
void PostRunAsynchronousTest() override { interceptor_.reset(); }
void RunDevTooledTest() override {
DCHECK(interceptor_);
interceptor_->InsertResponse("http://foo.com/index.html",
{kPageWhichOpensAWindow, "text/html"});
interceptor_->InsertResponse("http://foo.com/page2.html",
{kPage2, "text/html"});
devtools_client_.AddEventHandler(
"Runtime.consoleAPICalled",
base::BindRepeating(&WebContentsOpenTest::OnConsoleAPICalled,
base::Unretained(this)));
SendCommandSync(devtools_client_, "Runtime.enable");
devtools_client_.SendCommand("Page.navigate",
Param("url", "http://foo.com/index.html"));
}
virtual void OnConsoleAPICalled(const base::Value::Dict& params) {}
protected:
std::unique_ptr<TestNetworkInterceptor> interceptor_;
};
class DontBlockWebContentsOpenTest : public WebContentsOpenTest {
public:
void CustomizeHeadlessBrowserContext(
HeadlessBrowserContext::Builder& builder) override {
builder.SetBlockNewWebContents(false);
}
void OnConsoleAPICalled(const base::Value::Dict& params) override {
EXPECT_THAT(
interceptor_->urls_requested(),
ElementsAre("http://foo.com/index.html", "http://foo.com/page2.html"));
FinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest);
class BlockWebContentsOpenTest : public WebContentsOpenTest {
public:
void CustomizeHeadlessBrowserContext(
HeadlessBrowserContext::Builder& builder) override {
builder.SetBlockNewWebContents(true);
}
void OnConsoleAPICalled(const base::Value::Dict& params) override {
EXPECT_THAT(interceptor_->urls_requested(),
ElementsAre("http://foo.com/index.html"));
FinishAsynchronousTest();
}
};
HEADLESS_DEVTOOLED_TEST_F(BlockWebContentsOpenTest);
// Regression test for crbug.com/1385982.
class BlockDevToolsEmbedding : public HeadlessDevTooledBrowserTest {
protected:
void SetUpInProcessBrowserTestFixture() override {
HeadlessDevTooledBrowserTest::SetUpInProcessBrowserTestFixture();
options()->devtools_endpoint = net::HostPortPair("localhost", port_);
}
void RunDevTooledTest() override {
std::stringstream url;
url << "data:text/html,<iframe src='http://localhost:" << port_
<< "/json/version'></iframe>";
devtools_client_.AddEventHandler(
"Page.loadEventFired",
base::BindRepeating(&BlockDevToolsEmbedding::OnLoadEventFired,
base::Unretained(this)));
devtools_client_.SendCommand("Page.enable");
devtools_client_.SendCommand("Page.navigate", Param("url", url.str()));
}
void OnLoadEventFired(const base::Value::Dict& params) {
devtools_client_.SendCommand(
"Page.getFrameTree",
base::BindOnce(&BlockDevToolsEmbedding::OnFrameTreeResult,
base::Unretained(this)));
}
void OnFrameTreeResult(base::Value::Dict result) {
// Make sure the iframe did not load successfully.
auto& child_frames =
*result.FindListByDottedPath("result.frameTree.childFrames");
EXPECT_EQ(DictString(child_frames[0].GetDict(), "frame.url"),
"chrome-error://chromewebdata/");
FinishAsynchronousTest();
}
private:
int port_ = 10000 + (rand() % 60000);
};
HEADLESS_DEVTOOLED_TEST_F(BlockDevToolsEmbedding);
} // namespace headless