[go: nahoru, domu]

blob: 10ccaf3b2bf23a4fcd246d9fa0a5086067114afa [file] [log] [blame]
Avi Drissmane5b65ca92022-09-13 21:23:421// Copyright 2016 The Chromium Authors
skyostilfe116162016-02-26 20:53:332// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
mostynbc3d183b2016-04-04 11:02:455#include <memory>
eseckler9ef54762016-06-03 17:28:386#include <string>
7#include <vector>
mostynbc3d183b2016-04-04 11:02:458
Peter Kvitekc090ae62022-11-02 23:17:399#include "base/base64.h"
Hans Wennborg2b66ec12020-04-24 18:19:5010#include "base/check_op.h"
Eric Seckler80a868a2017-10-10 22:57:4411#include "base/command_line.h"
Avi Drissmancac43f22023-01-12 00:58:4112#include "base/functional/bind.h"
alexclarke1a4a3bab2017-05-24 16:07:4213#include "base/json/json_writer.h"
Keishi Hattori0e45c022021-11-27 09:25:5214#include "base/memory/raw_ptr.h"
Andrey Kosyakov9bfac972023-01-18 18:16:5015#include "base/strings/string_number_conversions.h"
Dominic Mazzoni3895ab02020-01-28 22:50:5316#include "base/strings/string_util.h"
Eric Seckler82ad0e92017-07-17 15:52:2217#include "build/build_config.h"
Eric Seckler4f0eb9fa2018-03-01 11:23:2118#include "cc/base/switches.h"
danakjcc838d42019-10-02 23:18:3219#include "cc/test/pixel_test_utils.h"
Peter Kvitekc090ae62022-11-02 23:17:3920#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
Eric Seckler5b74eba2018-02-21 18:41:4421#include "components/viz/common/switches.h"
Eric Seckler82ad0e92017-07-17 15:52:2222#include "content/public/browser/web_contents.h"
Eric Seckler7b9e9952017-11-14 19:08:3123#include "content/public/common/content_switches.h"
skyostilfe116162016-02-26 20:53:3324#include "content/public/test/browser_test.h"
Eric Seckler48f8a072017-06-20 14:28:3625#include "headless/lib/browser/headless_web_contents_impl.h"
skyostilfe116162016-02-26 20:53:3326#include "headless/public/headless_browser.h"
27#include "headless/public/headless_web_contents.h"
28#include "headless/test/headless_browser_test.h"
Peter Kvitek82ddd9a2022-10-28 19:18:1829#include "headless/test/headless_browser_test_utils.h"
Peter Kvitekc090ae62022-11-02 23:17:3930#include "headless/test/headless_devtooled_browsertest.h"
31#include "headless/test/test_network_interceptor.h"
altiminbf875c92016-08-04 17:09:0732#include "testing/gmock/include/gmock/gmock.h"
skyostilfe116162016-02-26 20:53:3333#include "testing/gtest/include/gtest/gtest.h"
Anton Bikineevaabbd6082021-05-15 17:57:2034#include "third_party/abseil-cpp/absl/types/optional.h"
Gyuyoung Kima252d4a2020-09-16 07:20:3135#include "third_party/blink/public/common/switches.h"
esecklerd2869ff2017-01-19 14:46:2136#include "third_party/skia/include/core/SkBitmap.h"
37#include "third_party/skia/include/core/SkColor.h"
38#include "ui/gfx/codec/png_codec.h"
skyostilfe116162016-02-26 20:53:3339#include "ui/gfx/geometry/size.h"
Daniel Hosseinian87f50e552020-09-08 17:52:2640#include "ui/gfx/geometry/size_f.h"
skyostilfe116162016-02-26 20:53:3341#include "url/gurl.h"
42
alexclarkefb9a2352017-05-25 14:19:1943using testing::ElementsAre;
Alex Clarke9155a342017-09-15 10:56:5944using testing::ElementsAreArray;
45using testing::Not;
altiminbf875c92016-08-04 17:09:0746using testing::UnorderedElementsAre;
Alex Clarke9155a342017-09-15 10:56:5947using testing::UnorderedElementsAreArray;
altiminbf875c92016-08-04 17:09:0748
Peter Kvitekc090ae62022-11-02 23:17:3949using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
50
skyostilfe116162016-02-26 20:53:3351namespace headless {
Peter Kvitekc090ae62022-11-02 23:17:3952
skyostilfe116162016-02-26 20:53:3353class HeadlessWebContentsTest : public HeadlessBrowserTest {};
54
Peter Kvitek47a3f472020-11-05 22:47:3855IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) {
skyostilfe116162016-02-26 20:53:3356 EXPECT_TRUE(embedded_test_server()->Start());
eseckler9ef54762016-06-03 17:28:3857
altiminbf875c92016-08-04 17:09:0758 HeadlessBrowserContext* browser_context =
altimin93a02402016-08-03 16:26:4359 browser()->CreateBrowserContextBuilder().Build();
60
skyostil25c2c0122016-06-09 12:54:1261 HeadlessWebContents* web_contents =
altimin93a02402016-08-03 16:26:4362 browser_context->CreateWebContentsBuilder()
skyostil25c2c0122016-06-09 12:54:1263 .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
64 .Build();
skyostil585fa602016-04-25 16:07:5665 EXPECT_TRUE(WaitForLoad(web_contents));
altiminb6d2d1fc2016-04-21 15:23:2266
altiminbf875c92016-08-04 17:09:0767 EXPECT_THAT(browser_context->GetAllWebContents(),
68 UnorderedElementsAre(web_contents));
altiminb6d2d1fc2016-04-21 15:23:2269}
70
Peter Kvitek47a3f472020-11-05 22:47:3871IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, WindowOpen) {
altiminb6d2d1fc2016-04-21 15:23:2272 EXPECT_TRUE(embedded_test_server()->Start());
73
altiminbf875c92016-08-04 17:09:0774 HeadlessBrowserContext* browser_context =
altimin93a02402016-08-03 16:26:4375 browser()->CreateBrowserContextBuilder().Build();
76
skyostil25c2c0122016-06-09 12:54:1277 HeadlessWebContents* web_contents =
altimin93a02402016-08-03 16:26:4378 browser_context->CreateWebContentsBuilder()
skyostil25c2c0122016-06-09 12:54:1279 .SetInitialURL(embedded_test_server()->GetURL("/window_open.html"))
80 .Build();
skyostil585fa602016-04-25 16:07:5681 EXPECT_TRUE(WaitForLoad(web_contents));
altiminb6d2d1fc2016-04-21 15:23:2282
Eric Seckler48f8a072017-06-20 14:28:3683 EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
84
Johannes Henkeled0e5da2018-05-30 22:55:1885 HeadlessWebContentsImpl* child = nullptr;
86 HeadlessWebContentsImpl* parent = nullptr;
87 for (HeadlessWebContents* c : browser_context->GetAllWebContents()) {
88 HeadlessWebContentsImpl* impl = HeadlessWebContentsImpl::From(c);
89 if (impl->window_id() == 1)
90 parent = impl;
91 else if (impl->window_id() == 2)
92 child = impl;
93 }
94
Eric Seckler48f8a072017-06-20 14:28:3695 EXPECT_NE(nullptr, parent);
96 EXPECT_NE(nullptr, child);
97 EXPECT_NE(parent, child);
98
99 // Mac doesn't have WindowTreeHosts.
100 if (parent && child && parent->window_tree_host())
101 EXPECT_NE(parent->window_tree_host(), child->window_tree_host());
Eric Seckler82ad0e92017-07-17 15:52:22102
103 gfx::Rect expected_bounds(0, 0, 200, 100);
Xiaohan Wang2d5a55e2022-01-08 01:34:16104#if !BUILDFLAG(IS_MAC)
Eric Seckler82ad0e92017-07-17 15:52:22105 EXPECT_EQ(expected_bounds, child->web_contents()->GetViewBounds());
106 EXPECT_EQ(expected_bounds, child->web_contents()->GetContainerBounds());
Xiaohan Wang2d5a55e2022-01-08 01:34:16107#else // !BUILDFLAG(IS_MAC)
Eric Seckler82ad0e92017-07-17 15:52:22108 // Mac does not support GetViewBounds() and view positions are random.
109 EXPECT_EQ(expected_bounds.size(),
110 child->web_contents()->GetContainerBounds().size());
Xiaohan Wang2d5a55e2022-01-08 01:34:16111#endif // !BUILDFLAG(IS_MAC)
Eric Seckler48f8a072017-06-20 14:28:36112}
113
Johannes Henkel03212fe2018-04-25 17:34:32114IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest,
Peter Kvitek47a3f472020-11-05 22:47:38115 FocusOfHeadlessWebContents_IsIndependent) {
irisu559af0912017-02-28 13:10:34116 EXPECT_TRUE(embedded_test_server()->Start());
117
118 HeadlessBrowserContext* browser_context =
119 browser()->CreateBrowserContextBuilder().Build();
120
121 HeadlessWebContents* web_contents =
122 browser_context->CreateWebContentsBuilder()
123 .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
124 .Build();
arthursonzognib3a06c1b2018-05-14 11:52:35125 WaitForLoadAndGainFocus(web_contents);
irisu559af0912017-02-28 13:10:34126
Peter Kvitekc090ae62022-11-02 23:17:39127 EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
128 DictHasValue("result.result.value", true));
irisu559af0912017-02-28 13:10:34129
130 HeadlessWebContents* web_contents2 =
131 browser_context->CreateWebContentsBuilder()
132 .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
133 .Build();
arthursonzognib3a06c1b2018-05-14 11:52:35134 WaitForLoadAndGainFocus(web_contents2);
irisu559af0912017-02-28 13:10:34135
esecklerc09e3992017-04-03 13:10:36136 // Focus of different WebContents is independent.
Peter Kvitekc090ae62022-11-02 23:17:39137 EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"),
138 DictHasValue("result.result.value", true));
139 EXPECT_THAT(EvaluateScript(web_contents2, "document.hasFocus()"),
140 DictHasValue("result.result.value", true));
irisu559af0912017-02-28 13:10:34141}
142
Peter Kvitek47a3f472020-11-05 22:47:38143IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, HandleSSLError) {
sushkovfd9f2942017-05-09 04:34:36144 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
145 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
146 ASSERT_TRUE(https_server.Start());
147
148 HeadlessBrowserContext* browser_context =
149 browser()->CreateBrowserContextBuilder().Build();
150
151 HeadlessWebContents* web_contents =
152 browser_context->CreateWebContentsBuilder()
153 .SetInitialURL(https_server.GetURL("/hello.html"))
154 .Build();
155
156 EXPECT_FALSE(WaitForLoad(web_contents));
157}
158
esecklerd2869ff2017-01-19 14:46:21159namespace {
Peter Kvitekc090ae62022-11-02 23:17:39160bool DecodePNG(const std::string& png_data, SkBitmap* bitmap) {
161 return gfx::PNGCodec::Decode(
162 reinterpret_cast<const unsigned char*>(png_data.data()), png_data.size(),
163 bitmap);
esecklerd2869ff2017-01-19 14:46:21164}
165} // namespace
166
167// Parameter specifies whether --disable-gpu should be used.
eseckler9ef54762016-06-03 17:28:38168class HeadlessWebContentsScreenshotTest
Peter Kvitekc090ae62022-11-02 23:17:39169 : public HeadlessDevTooledBrowserTest,
esecklerd2869ff2017-01-19 14:46:21170 public ::testing::WithParamInterface<bool> {
eseckler9ef54762016-06-03 17:28:38171 public:
esecklerd2869ff2017-01-19 14:46:21172 void SetUp() override {
173 EnablePixelOutput();
Sami Kyostilad99a9662017-06-06 09:09:17174 if (GetParam()) {
esecklerd2869ff2017-01-19 14:46:21175 UseSoftwareCompositing();
Sami Kyostilad99a9662017-06-06 09:09:17176 SetUpWithoutGPU();
177 } else {
Peter Kvitekc090ae62022-11-02 23:17:39178 HeadlessDevTooledBrowserTest::SetUp();
Sami Kyostilad99a9662017-06-06 09:09:17179 }
esecklerd2869ff2017-01-19 14:46:21180 }
181
eseckler9ef54762016-06-03 17:28:38182 void RunDevTooledTest() override {
Peter Kvitekc090ae62022-11-02 23:17:39183 devtools_client_.SendCommand(
184 "Runtime.evaluate",
185 Param("expression", "document.body.style.background = '#0000ff'"),
tzikf7adad1a2018-03-10 20:31:01186 base::BindOnce(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted,
187 base::Unretained(this)));
esecklerd2869ff2017-01-19 14:46:21188 }
189
Peter Kvitekc090ae62022-11-02 23:17:39190 void OnPageSetupCompleted(base::Value::Dict) {
191 devtools_client_.SendCommand(
192 "Page.captureScreenshot",
tzikf7adad1a2018-03-10 20:31:01193 base::BindOnce(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured,
194 base::Unretained(this)));
eseckler9ef54762016-06-03 17:28:38195 }
196
Peter Kvitekc090ae62022-11-02 23:17:39197 void OnScreenshotCaptured(base::Value::Dict result) {
198 std::string png_data_base64 = DictString(result, "result.data");
199 ASSERT_FALSE(png_data_base64.empty());
200
201 std::string png_data;
202 ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
Johannes Henkelc58a4aef2018-10-29 22:39:53203 EXPECT_GT(png_data.size(), 0U);
Peter Kvitekc090ae62022-11-02 23:17:39204
esecklerd2869ff2017-01-19 14:46:21205 SkBitmap result_bitmap;
Johannes Henkelc58a4aef2018-10-29 22:39:53206 EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
esecklerd2869ff2017-01-19 14:46:21207
208 EXPECT_EQ(800, result_bitmap.width());
209 EXPECT_EQ(600, result_bitmap.height());
210 SkColor actual_color = result_bitmap.getColor(400, 300);
211 SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
212 EXPECT_EQ(expected_color, actual_color);
Peter Kvitekc090ae62022-11-02 23:17:39213
eseckler9ef54762016-06-03 17:28:38214 FinishAsynchronousTest();
215 }
216};
217
Peter Kvitekc090ae62022-11-02 23:17:39218HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest);
esecklerd2869ff2017-01-19 14:46:21219
220// Instantiate test case for both software and gpu compositing modes.
Victor Costan33fe9782019-02-01 16:53:09221INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotTests,
222 HeadlessWebContentsScreenshotTest,
223 ::testing::Bool());
eseckler9ef54762016-06-03 17:28:38224
Eric Seckler1cbd8e02018-04-27 03:17:34225// Regression test for crbug.com/832138.
226class HeadlessWebContentsScreenshotWindowPositionTest
227 : public HeadlessWebContentsScreenshotTest {
228 public:
229 void RunDevTooledTest() override {
Peter Kvitekc090ae62022-11-02 23:17:39230 base::Value::Dict params;
231 params.Set("windowId",
232 HeadlessWebContentsImpl::From(web_contents_)->window_id());
233 params.SetByDottedPath("bounds.left", 600);
234 params.SetByDottedPath("bounds.top", 100);
235 params.SetByDottedPath("bounds.width", 800);
236 params.SetByDottedPath("bounds.height", 600);
237
238 browser_devtools_client_.SendCommand(
239 "Browser.setWindowBounds", std::move(params),
Eric Seckler1cbd8e02018-04-27 03:17:34240 base::BindOnce(
241 &HeadlessWebContentsScreenshotWindowPositionTest::OnWindowBoundsSet,
242 base::Unretained(this)));
243 }
244
Peter Kvitekc090ae62022-11-02 23:17:39245 void OnWindowBoundsSet(base::Value::Dict result) {
246 EXPECT_NE(result.FindDict("result"), nullptr);
Eric Seckler1cbd8e02018-04-27 03:17:34247 HeadlessWebContentsScreenshotTest::RunDevTooledTest();
248 }
249};
250
Xiaohan Wang2d5a55e2022-01-08 01:34:16251#if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER)
Jan Wilken Dörriedb00e4822020-05-27 10:27:08252// TODO(crbug.com/1086872): Disabled due to flakiness on Mac ASAN.
Peter Kvitekc090ae62022-11-02 23:17:39253DISABLED_HEADLESS_DEVTOOLED_TEST_P(
James Cook8a55e3432021-03-25 00:08:07254 HeadlessWebContentsScreenshotWindowPositionTest);
255#else
Peter Kvitekc090ae62022-11-02 23:17:39256HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotWindowPositionTest);
James Cook8a55e3432021-03-25 00:08:07257#endif
258
259// Instantiate test case for both software and gpu compositing modes.
Victor Costan33fe9782019-02-01 16:53:09260INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotWindowPositionTests,
261 HeadlessWebContentsScreenshotWindowPositionTest,
262 ::testing::Bool());
Eric Seckler1cbd8e02018-04-27 03:17:34263
Eric Secklerea51c9c2017-07-18 20:47:56264// Regression test for https://crbug.com/733569.
265class HeadlessWebContentsRequestStorageQuotaTest
Peter Kvitekc090ae62022-11-02 23:17:39266 : public HeadlessDevTooledBrowserTest {
Eric Secklerea51c9c2017-07-18 20:47:56267 public:
268 void RunDevTooledTest() override {
269 EXPECT_TRUE(embedded_test_server()->Start());
270
Peter Kvitekc090ae62022-11-02 23:17:39271 devtools_client_.AddEventHandler(
272 "Runtime.consoleAPICalled",
273 base::BindRepeating(
274 &HeadlessWebContentsRequestStorageQuotaTest::OnConsoleAPICalled,
275 base::Unretained(this)));
276 SendCommandSync(devtools_client_, "Runtime.enable");
Eric Secklerea51c9c2017-07-18 20:47:56277
278 // Should not crash and call console.log() if quota request succeeds.
Peter Kvitekc090ae62022-11-02 23:17:39279 devtools_client_.SendCommand(
280 "Page.navigate",
281 Param("url", embedded_test_server()
282 ->GetURL("/request_storage_quota.html")
283 .spec()));
Eric Secklerea51c9c2017-07-18 20:47:56284 }
285
Peter Kvitekc090ae62022-11-02 23:17:39286 void OnConsoleAPICalled(const base::Value::Dict& params) {
287 const base::Value::List* args = params.FindListByDottedPath("params.args");
288 ASSERT_NE(args, nullptr);
289 ASSERT_GT(args->size(), 0ul);
290
291 const base::Value* value = args->front().GetDict().Find("value");
292 ASSERT_NE(value, nullptr);
293 EXPECT_EQ(value->GetString(), "success");
294
Eric Secklerea51c9c2017-07-18 20:47:56295 FinishAsynchronousTest();
296 }
297};
298
Peter Kvitekc090ae62022-11-02 23:17:39299HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsRequestStorageQuotaTest);
Eric Secklerea51c9c2017-07-18 20:47:56300
Peter Kvitek47a3f472020-11-05 22:47:38301IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserTabChangeContent) {
Luca Versari4128efc2017-08-04 13:33:22302 EXPECT_TRUE(embedded_test_server()->Start());
303
304 HeadlessBrowserContext* browser_context =
305 browser()->CreateBrowserContextBuilder().Build();
306
307 HeadlessWebContents* web_contents =
308 browser_context->CreateWebContentsBuilder().Build();
309 EXPECT_TRUE(WaitForLoad(web_contents));
310
311 std::string script = "window.location = '" +
312 embedded_test_server()->GetURL("/hello.html").spec() +
313 "';";
Peter Kvitekc090ae62022-11-02 23:17:39314 EXPECT_THAT(EvaluateScript(web_contents, script),
315 Not(DictHasKey("exceptionDetails")));
Luca Versari4128efc2017-08-04 13:33:22316
317 // This will time out if the previous script did not work.
318 EXPECT_TRUE(WaitForLoad(web_contents));
319}
320
Peter Kvitek47a3f472020-11-05 22:47:38321IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserOpenInTab) {
Luca Versari4128efc2017-08-04 13:33:22322 EXPECT_TRUE(embedded_test_server()->Start());
323
324 HeadlessBrowserContext* browser_context =
325 browser()->CreateBrowserContextBuilder().Build();
326
Luca Versari4128efc2017-08-04 13:33:22327 HeadlessWebContents* web_contents =
328 browser_context->CreateWebContentsBuilder()
329 .SetInitialURL(embedded_test_server()->GetURL("/link.html"))
330 .Build();
331 EXPECT_TRUE(WaitForLoad(web_contents));
332
333 EXPECT_EQ(1u, browser_context->GetAllWebContents().size());
334 // Simulates a middle-button click on a link to ensure that the
335 // link is opened in a new tab by the browser and not by the renderer.
336 std::string script =
337 "var event = new MouseEvent('click', {'button': 1});"
338 "document.getElementsByTagName('a')[0].dispatchEvent(event);";
Peter Kvitekc090ae62022-11-02 23:17:39339 EXPECT_THAT(EvaluateScript(web_contents, script),
340 Not(DictHasKey("exceptionDetails")));
Luca Versari4128efc2017-08-04 13:33:22341 // Check that we have a new tab.
342 EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
Luca Versari4128efc2017-08-04 13:33:22343}
Alex Clarke9155a342017-09-15 10:56:59344
Eric Seckler80a868a2017-10-10 22:57:44345// BeginFrameControl is not supported on MacOS.
Xiaohan Wang2d5a55e2022-01-08 01:34:16346#if !BUILDFLAG(IS_MAC)
Eric Seckler80a868a2017-10-10 22:57:44347
Peter Kvitekc090ae62022-11-02 23:17:39348// TODO(kvitekp): Check to see if this could be trimmed down by using
349// Pre/PostRunAsynchronousTest().
350class HeadlessWebContentsBeginFrameControlTest : public HeadlessBrowserTest {
Eric Seckler80a868a2017-10-10 22:57:44351 public:
Eric Seckler4948f932018-08-28 14:51:22352 HeadlessWebContentsBeginFrameControlTest() {}
Eric Seckler80a868a2017-10-10 22:57:44353
354 void SetUp() override {
355 EnablePixelOutput();
356 HeadlessBrowserTest::SetUp();
357 }
358
359 protected:
360 virtual std::string GetTestHtmlFile() = 0;
Andrey Kosyakov7d500492022-11-03 23:33:48361 virtual void StartFrames() {}
Peter Kvitekc090ae62022-11-02 23:17:39362 virtual void OnFrameFinished(base::Value::Dict result) {}
Eric Seckler80a868a2017-10-10 22:57:44363
364 void SetUpCommandLine(base::CommandLine* command_line) override {
365 HeadlessBrowserTest::SetUpCommandLine(command_line);
Eric Seckler4f0eb9fa2018-03-01 11:23:21366 // See bit.ly/headless-rendering for why we use these flags.
Dominic Mazzoni3895ab02020-01-28 22:50:53367 command_line->AppendSwitch(::switches::kRunAllCompositorStagesBeforeDraw);
368 command_line->AppendSwitch(::switches::kDisableNewContentRenderingTimeout);
Eric Seckler4f0eb9fa2018-03-01 11:23:21369 command_line->AppendSwitch(cc::switches::kDisableCheckerImaging);
370 command_line->AppendSwitch(cc::switches::kDisableThreadedAnimation);
Eric Seckler80a868a2017-10-10 22:57:44371 }
372
Peter Kvitekc090ae62022-11-02 23:17:39373 void RunTest() {
Andrey Kosyakovee999242023-06-30 02:26:26374 browser()->SetDefaultBrowserContext(
375 browser()->CreateBrowserContextBuilder().Build());
376 SimpleDevToolsProtocolClient browser_devtools_client;
377 browser_devtools_client.AttachToBrowser();
Eric Seckler80a868a2017-10-10 22:57:44378
Peter Kvitekc090ae62022-11-02 23:17:39379 EXPECT_TRUE(embedded_test_server()->Start());
Eric Seckler80a868a2017-10-10 22:57:44380
Peter Kvitekc090ae62022-11-02 23:17:39381 base::Value::Dict params;
382 params.Set("url", "about:blank");
383 params.Set("width", 200);
384 params.Set("height", 200);
385 params.Set("enableBeginFrameControl", true);
Andrey Kosyakovee999242023-06-30 02:26:26386 browser_devtools_client.SendCommand(
Peter Kvitekc090ae62022-11-02 23:17:39387 "Target.createTarget", std::move(params),
tzikf7adad1a2018-03-10 20:31:01388 base::BindOnce(
Peter Kvitekc090ae62022-11-02 23:17:39389 &HeadlessWebContentsBeginFrameControlTest::OnTargetCreated,
390 base::Unretained(this)));
391
392 RunAsynchronousTest();
393
Andrey Kosyakovee999242023-06-30 02:26:26394 browser_devtools_client.DetachClient();
Peter Kvitekc090ae62022-11-02 23:17:39395 }
396
397 void OnTargetCreated(base::Value::Dict result) {
398 const std::string targetId = DictString(result, "result.targetId");
399 ASSERT_FALSE(targetId.empty());
400
401 web_contents_ = HeadlessWebContentsImpl::From(
402 browser()->GetWebContentsForDevToolsAgentHostId(targetId));
403
404 devtools_client_.AttachToWebContents(web_contents_->web_contents());
Peter Kvitekc090ae62022-11-02 23:17:39405 devtools_client_.AddEventHandler("Page.loadEventFired",
406 on_load_event_fired_handler_);
407
408 devtools_client_.SendCommand(
409 "Page.enable",
410 base::BindOnce(
411 &HeadlessWebContentsBeginFrameControlTest::OnPageDomainEnabled,
412 base::Unretained(this)));
Eric Seckler80a868a2017-10-10 22:57:44413 }
414
Peter Kvitekc090ae62022-11-02 23:17:39415 void OnPageDomainEnabled(base::Value::Dict) {
416 devtools_client_.SendCommand(
417 "Page.navigate",
418 Param("url", embedded_test_server()->GetURL(GetTestHtmlFile()).spec()));
Eric Seckler80a868a2017-10-10 22:57:44419 }
420
Peter Kvitekc090ae62022-11-02 23:17:39421 void OnLoadEventFired(const base::Value::Dict& params) {
Eric Seckler80a868a2017-10-10 22:57:44422 TRACE_EVENT0("headless",
423 "HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired");
Peter Kvitekc090ae62022-11-02 23:17:39424
425 devtools_client_.SendCommand("Page.disable");
426 devtools_client_.RemoveEventHandler("Page.loadEventFired",
427 on_load_event_fired_handler_);
428
Andrey Kosyakov7d500492022-11-03 23:33:48429 StartFrames();
Eric Seckler80a868a2017-10-10 22:57:44430 }
431
Eric Seckler80a868a2017-10-10 22:57:44432 void BeginFrame(bool screenshot) {
Eric Seckler4f0eb9fa2018-03-01 11:23:21433 num_begin_frames_++;
Eric Seckler80a868a2017-10-10 22:57:44434
Peter Kvitekc090ae62022-11-02 23:17:39435 base::Value::Dict params;
436 if (screenshot)
437 params.Set("screenshot", base::Value::Dict());
Eric Seckler80a868a2017-10-10 22:57:44438
Peter Kvitekc090ae62022-11-02 23:17:39439 devtools_client_.SendCommand(
440 "HeadlessExperimental.beginFrame", std::move(params),
tzikf7adad1a2018-03-10 20:31:01441 base::BindOnce(&HeadlessWebContentsBeginFrameControlTest::FrameFinished,
442 base::Unretained(this)));
Eric Seckler80a868a2017-10-10 22:57:44443 }
444
Peter Kvitekc090ae62022-11-02 23:17:39445 void FrameFinished(base::Value::Dict result) {
446 TRACE_EVENT2(
447 "headless", "HeadlessWebContentsBeginFrameControlTest::FrameFinished",
448 "has_damage", DictBool(result, "result.hasDamage"),
449 "has_screenshot_data", DictString(result, "result.screenshotData"));
Eric Seckler80a868a2017-10-10 22:57:44450
Eric Seckler80a868a2017-10-10 22:57:44451 OnFrameFinished(std::move(result));
452 }
453
454 void PostFinishAsynchronousTest() {
Eric Seckler80a868a2017-10-10 22:57:44455 browser()->BrowserMainThread()->PostTask(
456 FROM_HERE,
457 base::BindOnce(
458 &HeadlessWebContentsBeginFrameControlTest::FinishAsynchronousTest,
459 base::Unretained(this)));
460 }
461
Pârise6361d02023-07-19 09:00:43462 raw_ptr<HeadlessWebContentsImpl, AcrossTasksDanglingUntriaged> web_contents_ =
Ali Hijazi4d4e24092022-10-20 22:59:31463 nullptr; // Not owned.
Eric Seckler80a868a2017-10-10 22:57:44464
Eric Seckler4f0eb9fa2018-03-01 11:23:21465 int num_begin_frames_ = 0;
Peter Kvitekc090ae62022-11-02 23:17:39466
Peter Kvitekc090ae62022-11-02 23:17:39467 SimpleDevToolsProtocolClient devtools_client_;
468
469 SimpleDevToolsProtocolClient::EventCallback on_load_event_fired_handler_ =
470 base::BindRepeating(
471 &HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired,
472 base::Unretained(this));
Eric Seckler80a868a2017-10-10 22:57:44473};
474
475class HeadlessWebContentsBeginFrameControlBasicTest
476 : public HeadlessWebContentsBeginFrameControlTest {
477 public:
Chris Watkins6e89eee2017-11-29 06:01:18478 HeadlessWebContentsBeginFrameControlBasicTest() = default;
Eric Seckler80a868a2017-10-10 22:57:44479
480 protected:
481 std::string GetTestHtmlFile() override {
482 // Blue background.
483 return "/blue_page.html";
484 }
485
Andrey Kosyakov7d500492022-11-03 23:33:48486 void StartFrames() override { BeginFrame(true); }
Eric Seckler80a868a2017-10-10 22:57:44487
Peter Kvitekc090ae62022-11-02 23:17:39488 void OnFrameFinished(base::Value::Dict result) override {
Eric Seckler4f0eb9fa2018-03-01 11:23:21489 if (num_begin_frames_ == 1) {
Saman Sami5c236082018-10-11 19:15:07490 // First BeginFrame should have caused damage and have a screenshot.
Peter Kvitekc090ae62022-11-02 23:17:39491 EXPECT_TRUE(DictBool(result, "result.hasDamage"));
492
493 std::string png_data_base64 = DictString(result, "result.screenshotData");
494 ASSERT_FALSE(png_data_base64.empty());
495
496 std::string png_data;
497 ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
498 EXPECT_GT(png_data.size(), 0U);
499
Saman Sami5c236082018-10-11 19:15:07500 SkBitmap result_bitmap;
Johannes Henkelc58a4aef2018-10-29 22:39:53501 EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
Saman Sami5c236082018-10-11 19:15:07502 EXPECT_EQ(200, result_bitmap.width());
503 EXPECT_EQ(200, result_bitmap.height());
504 SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
505 SkColor actual_color = result_bitmap.getColor(100, 100);
506 EXPECT_EQ(expected_color, actual_color);
Eric Seckler4f0eb9fa2018-03-01 11:23:21507 } else {
Saman Sami5c236082018-10-11 19:15:07508 DCHECK_EQ(2, num_begin_frames_);
509 // Can't guarantee that the second BeginFrame didn't have damage, but it
Eric Seckler4f0eb9fa2018-03-01 11:23:21510 // should not have a screenshot.
Peter Kvitekc090ae62022-11-02 23:17:39511 EXPECT_FALSE(result.FindStringByDottedPath("result.screenshotData"));
Eric Seckler4f0eb9fa2018-03-01 11:23:21512 }
Eric Seckler80a868a2017-10-10 22:57:44513
Saman Sami5c236082018-10-11 19:15:07514 if (num_begin_frames_ < 2) {
515 // Don't capture a screenshot in the second BeginFrame.
516 BeginFrame(false);
Eric Seckler4f0eb9fa2018-03-01 11:23:21517 } else {
Eric Seckler80a868a2017-10-10 22:57:44518 // Post completion to avoid deleting the WebContents on the same callstack
519 // as frame finished callback.
520 PostFinishAsynchronousTest();
521 }
522 }
Eric Seckler80a868a2017-10-10 22:57:44523};
524
Peter Kvitekc090ae62022-11-02 23:17:39525HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest);
Eric Seckler80a868a2017-10-10 22:57:44526
Eric Seckler80a868a2017-10-10 22:57:44527class HeadlessWebContentsBeginFrameControlViewportTest
528 : public HeadlessWebContentsBeginFrameControlTest {
529 public:
Chris Watkins6e89eee2017-11-29 06:01:18530 HeadlessWebContentsBeginFrameControlViewportTest() = default;
Eric Seckler80a868a2017-10-10 22:57:44531
532 protected:
533 std::string GetTestHtmlFile() override {
534 // Draws a 100x100px blue box at 200x200px.
535 return "/blue_box.html";
536 }
537
Andrey Kosyakov7d500492022-11-03 23:33:48538 void StartFrames() override {
Eric Seckler4f0eb9fa2018-03-01 11:23:21539 // Send a first BeginFrame to initialize the surface.
540 BeginFrame(false);
Eric Seckler80a868a2017-10-10 22:57:44541 }
542
543 void SetUpViewport() {
Peter Kvitekc090ae62022-11-02 23:17:39544 base::Value::Dict params;
545 params.Set("width", 0);
546 params.Set("height", 0);
547 params.Set("deviceScaleFactor", 0);
548 params.Set("mobile", false);
549 params.SetByDottedPath("viewport.x", 200);
550 params.SetByDottedPath("viewport.y", 200);
551 params.SetByDottedPath("viewport.width", 100);
552 params.SetByDottedPath("viewport.height", 100);
553 params.SetByDottedPath("viewport.scale", 3);
554
555 devtools_client_.SendCommand(
556 "Emulation.setDeviceMetricsOverride", std::move(params),
557 base::BindOnce(&HeadlessWebContentsBeginFrameControlViewportTest::
558 OnSetDeviceMetricsOverrideDone,
559 base::Unretained(this)));
Eric Seckler7b9e9952017-11-14 19:08:31560 }
561
Peter Kvitekc090ae62022-11-02 23:17:39562 void OnSetDeviceMetricsOverrideDone(base::Value::Dict result) {
563 EXPECT_THAT(result, DictHasKey("result"));
Saman Sami33be507c2018-03-02 22:33:08564 // Take a screenshot in the second BeginFrame.
565 BeginFrame(true);
Eric Seckler80a868a2017-10-10 22:57:44566 }
567
Peter Kvitekc090ae62022-11-02 23:17:39568 void OnFrameFinished(base::Value::Dict result) override {
Eric Seckler4f0eb9fa2018-03-01 11:23:21569 if (num_begin_frames_ == 1) {
Eric Seckler4f0eb9fa2018-03-01 11:23:21570 SetUpViewport();
571 return;
Eric Seckler4f0eb9fa2018-03-01 11:23:21572 }
573
Saman Sami33be507c2018-03-02 22:33:08574 DCHECK_EQ(2, num_begin_frames_);
Eric Seckler4f0eb9fa2018-03-01 11:23:21575 // Second BeginFrame should have a screenshot of the configured viewport and
576 // of the correct size.
Peter Kvitekc090ae62022-11-02 23:17:39577 EXPECT_TRUE(*result.FindBoolByDottedPath("result.hasDamage"));
Eric Seckler4f0eb9fa2018-03-01 11:23:21578
Peter Kvitekc090ae62022-11-02 23:17:39579 std::string png_data_base64 = DictString(result, "result.screenshotData");
580 ASSERT_FALSE(png_data_base64.empty());
Eric Seckler4f0eb9fa2018-03-01 11:23:21581
Peter Kvitekc090ae62022-11-02 23:17:39582 std::string png_data;
583 ASSERT_TRUE(base::Base64Decode(png_data_base64, &png_data));
584 ASSERT_GT(png_data.size(), 0ul);
585
586 SkBitmap result_bitmap;
587 EXPECT_TRUE(DecodePNG(png_data, &result_bitmap));
588
589 // Expect a 300x300 bitmap that is all blue.
590 SkBitmap expected_bitmap;
591 SkImageInfo info;
592 expected_bitmap.allocPixels(
593 SkImageInfo::MakeN32(300, 300, kOpaque_SkAlphaType), /*rowBytes=*/0);
594 expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
595
Xianzhu Wang43fe4442023-01-24 23:59:09596 EXPECT_TRUE(cc::MatchesBitmap(result_bitmap, expected_bitmap,
597 cc::ExactPixelComparator()));
Eric Seckler4f0eb9fa2018-03-01 11:23:21598
599 // Post completion to avoid deleting the WebContents on the same callstack
600 // as frame finished callback.
601 PostFinishAsynchronousTest();
602 }
Eric Seckler80a868a2017-10-10 22:57:44603};
604
Frank Liberato93ec02b2023-06-29 23:20:33605// TODO(crbug.com/1459385): Turning this off since it's flaking regularly.
606DISABLED_HEADLESS_DEVTOOLED_TEST_F(
607 HeadlessWebContentsBeginFrameControlViewportTest);
Eric Seckler80a868a2017-10-10 22:57:44608
Xiaohan Wang2d5a55e2022-01-08 01:34:16609#endif // !BUILDFLAG(IS_MAC)
Eric Seckler80a868a2017-10-10 22:57:44610
Peter Kvitekc090ae62022-11-02 23:17:39611class CookiesEnabled : public HeadlessDevTooledBrowserTest {
Alex Clarke61c3cd72017-10-17 09:31:22612 public:
613 void RunDevTooledTest() override {
Alex Clarke61c3cd72017-10-17 09:31:22614 EXPECT_TRUE(embedded_test_server()->Start());
Peter Kvitekc090ae62022-11-02 23:17:39615
616 devtools_client_.AddEventHandler(
617 "Page.loadEventFired",
618 base::BindRepeating(&CookiesEnabled::OnLoadEventFired,
619 base::Unretained(this)));
620 devtools_client_.SendCommand("Page.enable");
621
622 devtools_client_.SendCommand(
623 "Page.navigate",
624 Param("url", embedded_test_server()->GetURL("/cookie.html").spec()));
Alex Clarke61c3cd72017-10-17 09:31:22625 }
626
Peter Kvitekc090ae62022-11-02 23:17:39627 void OnLoadEventFired(const base::Value::Dict& params) {
628 devtools_client_.SendCommand(
629 "Runtime.evaluate", Param("expression", "window.test_result"),
630 base::BindOnce(&CookiesEnabled::OnEvaluateResult,
631 base::Unretained(this)));
Alex Clarke61c3cd72017-10-17 09:31:22632 }
633
Peter Kvitekc090ae62022-11-02 23:17:39634 void OnEvaluateResult(base::Value::Dict result) {
635 EXPECT_EQ(DictString(result, "result.result.value"), "0");
636
Alex Clarke61c3cd72017-10-17 09:31:22637 FinishAsynchronousTest();
638 }
Alex Clarke61c3cd72017-10-17 09:31:22639};
640
Peter Kvitekc090ae62022-11-02 23:17:39641HEADLESS_DEVTOOLED_TEST_F(CookiesEnabled);
Alex Clarke61c3cd72017-10-17 09:31:22642
Alex Clarke6bd0fd52018-02-12 16:08:25643namespace {
644const char* kPageWhichOpensAWindow = R"(
645<html>
646<body>
647<script>
Andrey Kosyakov5ef20212020-09-17 21:44:17648const win = window.open('/page2.html');
649if (!win)
650 console.error('ready');
651win.addEventListener('load', () => console.log('ready'));
Alex Clarke6bd0fd52018-02-12 16:08:25652</script>
653</body>
654</html>
655)";
656
657const char* kPage2 = R"(
658<html>
659<body>
660Page 2.
661</body>
662</html>
663)";
664} // namespace
665
Peter Kvitekc090ae62022-11-02 23:17:39666class WebContentsOpenTest : public HeadlessDevTooledBrowserTest {
Alex Clarke6bd0fd52018-02-12 16:08:25667 public:
Peter Kvitekc090ae62022-11-02 23:17:39668 void PreRunAsynchronousTest() override {
669 interceptor_ = std::make_unique<TestNetworkInterceptor>();
670 }
671
672 void PostRunAsynchronousTest() override { interceptor_.reset(); }
673
Alex Clarke6bd0fd52018-02-12 16:08:25674 void RunDevTooledTest() override {
Peter Kvitekc090ae62022-11-02 23:17:39675 DCHECK(interceptor_);
676
Andrey Kosyakov8846844e2018-07-10 00:49:22677 interceptor_->InsertResponse("http://foo.com/index.html",
678 {kPageWhichOpensAWindow, "text/html"});
679 interceptor_->InsertResponse("http://foo.com/page2.html",
680 {kPage2, "text/html"});
Alex Clarke6bd0fd52018-02-12 16:08:25681
Peter Kvitekc090ae62022-11-02 23:17:39682 devtools_client_.AddEventHandler(
683 "Runtime.consoleAPICalled",
684 base::BindRepeating(&WebContentsOpenTest::OnConsoleAPICalled,
685 base::Unretained(this)));
686 SendCommandSync(devtools_client_, "Runtime.enable");
Alex Clarke6bd0fd52018-02-12 16:08:25687
Peter Kvitekc090ae62022-11-02 23:17:39688 devtools_client_.SendCommand("Page.navigate",
689 Param("url", "http://foo.com/index.html"));
Alex Clarke6bd0fd52018-02-12 16:08:25690 }
Peter Kvitekc090ae62022-11-02 23:17:39691
692 virtual void OnConsoleAPICalled(const base::Value::Dict& params) {}
693
694 protected:
695 std::unique_ptr<TestNetworkInterceptor> interceptor_;
Alex Clarke6bd0fd52018-02-12 16:08:25696};
697
698class DontBlockWebContentsOpenTest : public WebContentsOpenTest {
699 public:
700 void CustomizeHeadlessBrowserContext(
701 HeadlessBrowserContext::Builder& builder) override {
702 builder.SetBlockNewWebContents(false);
703 }
704
Peter Kvitekc090ae62022-11-02 23:17:39705 void OnConsoleAPICalled(const base::Value::Dict& params) override {
Alex Clarke6bd0fd52018-02-12 16:08:25706 EXPECT_THAT(
Andrey Kosyakov8846844e2018-07-10 00:49:22707 interceptor_->urls_requested(),
Alex Clarke6bd0fd52018-02-12 16:08:25708 ElementsAre("http://foo.com/index.html", "http://foo.com/page2.html"));
709 FinishAsynchronousTest();
710 }
711};
712
Peter Kvitekc090ae62022-11-02 23:17:39713HEADLESS_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest);
Alex Clarke6bd0fd52018-02-12 16:08:25714
715class BlockWebContentsOpenTest : public WebContentsOpenTest {
716 public:
717 void CustomizeHeadlessBrowserContext(
718 HeadlessBrowserContext::Builder& builder) override {
719 builder.SetBlockNewWebContents(true);
720 }
721
Peter Kvitekc090ae62022-11-02 23:17:39722 void OnConsoleAPICalled(const base::Value::Dict& params) override {
Andrey Kosyakov8846844e2018-07-10 00:49:22723 EXPECT_THAT(interceptor_->urls_requested(),
Alex Clarke6bd0fd52018-02-12 16:08:25724 ElementsAre("http://foo.com/index.html"));
725 FinishAsynchronousTest();
726 }
727};
728
Peter Kvitekc090ae62022-11-02 23:17:39729HEADLESS_DEVTOOLED_TEST_F(BlockWebContentsOpenTest);
Alex Clarke6bd0fd52018-02-12 16:08:25730
Sami Kyostila5606dfd2022-11-30 02:47:01731// Regression test for crbug.com/1385982.
732class BlockDevToolsEmbedding : public HeadlessDevTooledBrowserTest {
733 protected:
Andrey Kosyakov9bfac972023-01-18 18:16:50734 void SetUpCommandLine(base::CommandLine* command_line) override {
735 HeadlessDevTooledBrowserTest::SetUpCommandLine(command_line);
736 command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort,
737 base::NumberToString(port_));
Sami Kyostila5606dfd2022-11-30 02:47:01738 }
739
740 void RunDevTooledTest() override {
741 std::stringstream url;
742 url << "data:text/html,<iframe src='http://localhost:" << port_
743 << "/json/version'></iframe>";
744
745 devtools_client_.AddEventHandler(
746 "Page.loadEventFired",
747 base::BindRepeating(&BlockDevToolsEmbedding::OnLoadEventFired,
748 base::Unretained(this)));
749 devtools_client_.SendCommand("Page.enable");
750 devtools_client_.SendCommand("Page.navigate", Param("url", url.str()));
751 }
752
753 void OnLoadEventFired(const base::Value::Dict& params) {
754 devtools_client_.SendCommand(
755 "Page.getFrameTree",
756 base::BindOnce(&BlockDevToolsEmbedding::OnFrameTreeResult,
757 base::Unretained(this)));
758 }
759
760 void OnFrameTreeResult(base::Value::Dict result) {
761 // Make sure the iframe did not load successfully.
762 auto& child_frames =
763 *result.FindListByDottedPath("result.frameTree.childFrames");
764 EXPECT_EQ(DictString(child_frames[0].GetDict(), "frame.url"),
765 "chrome-error://chromewebdata/");
766 FinishAsynchronousTest();
767 }
768
769 private:
770 int port_ = 10000 + (rand() % 60000);
771};
772
773HEADLESS_DEVTOOLED_TEST_F(BlockDevToolsEmbedding);
774
skyostilfe116162016-02-26 20:53:33775} // namespace headless