Avi Drissman | e5b65ca9 | 2022-09-13 21:23:42 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
mostynb | c3d183b | 2016-04-04 11:02:45 | [diff] [blame] | 5 | #include <memory> |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 6 | #include <string> |
| 7 | #include <vector> |
mostynb | c3d183b | 2016-04-04 11:02:45 | [diff] [blame] | 8 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 9 | #include "base/base64.h" |
Hans Wennborg | 2b66ec1 | 2020-04-24 18:19:50 | [diff] [blame] | 10 | #include "base/check_op.h" |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 11 | #include "base/command_line.h" |
Avi Drissman | cac43f2 | 2023-01-12 00:58:41 | [diff] [blame] | 12 | #include "base/functional/bind.h" |
alexclarke | 1a4a3bab | 2017-05-24 16:07:42 | [diff] [blame] | 13 | #include "base/json/json_writer.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 14 | #include "base/memory/raw_ptr.h" |
Andrey Kosyakov | 9bfac97 | 2023-01-18 18:16:50 | [diff] [blame] | 15 | #include "base/strings/string_number_conversions.h" |
Dominic Mazzoni | 3895ab0 | 2020-01-28 22:50:53 | [diff] [blame] | 16 | #include "base/strings/string_util.h" |
Eric Seckler | 82ad0e9 | 2017-07-17 15:52:22 | [diff] [blame] | 17 | #include "build/build_config.h" |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 18 | #include "cc/base/switches.h" |
danakj | cc838d4 | 2019-10-02 23:18:32 | [diff] [blame] | 19 | #include "cc/test/pixel_test_utils.h" |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 20 | #include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h" |
Eric Seckler | 5b74eba | 2018-02-21 18:41:44 | [diff] [blame] | 21 | #include "components/viz/common/switches.h" |
Eric Seckler | 82ad0e9 | 2017-07-17 15:52:22 | [diff] [blame] | 22 | #include "content/public/browser/web_contents.h" |
Eric Seckler | 7b9e995 | 2017-11-14 19:08:31 | [diff] [blame] | 23 | #include "content/public/common/content_switches.h" |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 24 | #include "content/public/test/browser_test.h" |
Eric Seckler | 48f8a07 | 2017-06-20 14:28:36 | [diff] [blame] | 25 | #include "headless/lib/browser/headless_web_contents_impl.h" |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 26 | #include "headless/public/headless_browser.h" |
| 27 | #include "headless/public/headless_web_contents.h" |
| 28 | #include "headless/test/headless_browser_test.h" |
Peter Kvitek | 82ddd9a | 2022-10-28 19:18:18 | [diff] [blame] | 29 | #include "headless/test/headless_browser_test_utils.h" |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 30 | #include "headless/test/headless_devtooled_browsertest.h" |
| 31 | #include "headless/test/test_network_interceptor.h" |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 32 | #include "testing/gmock/include/gmock/gmock.h" |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 33 | #include "testing/gtest/include/gtest/gtest.h" |
Anton Bikineev | aabbd608 | 2021-05-15 17:57:20 | [diff] [blame] | 34 | #include "third_party/abseil-cpp/absl/types/optional.h" |
Gyuyoung Kim | a252d4a | 2020-09-16 07:20:31 | [diff] [blame] | 35 | #include "third_party/blink/public/common/switches.h" |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 36 | #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" |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 39 | #include "ui/gfx/geometry/size.h" |
Daniel Hosseinian | 87f50e55 | 2020-09-08 17:52:26 | [diff] [blame] | 40 | #include "ui/gfx/geometry/size_f.h" |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 41 | #include "url/gurl.h" |
| 42 | |
alexclarke | fb9a235 | 2017-05-25 14:19:19 | [diff] [blame] | 43 | using testing::ElementsAre; |
Alex Clarke | 9155a34 | 2017-09-15 10:56:59 | [diff] [blame] | 44 | using testing::ElementsAreArray; |
| 45 | using testing::Not; |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 46 | using testing::UnorderedElementsAre; |
Alex Clarke | 9155a34 | 2017-09-15 10:56:59 | [diff] [blame] | 47 | using testing::UnorderedElementsAreArray; |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 48 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 49 | using simple_devtools_protocol_client::SimpleDevToolsProtocolClient; |
| 50 | |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 51 | namespace headless { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 52 | |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 53 | class HeadlessWebContentsTest : public HeadlessBrowserTest {}; |
| 54 | |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 55 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) { |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 56 | EXPECT_TRUE(embedded_test_server()->Start()); |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 57 | |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 58 | HeadlessBrowserContext* browser_context = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 59 | browser()->CreateBrowserContextBuilder().Build(); |
| 60 | |
skyostil | 25c2c012 | 2016-06-09 12:54:12 | [diff] [blame] | 61 | HeadlessWebContents* web_contents = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 62 | browser_context->CreateWebContentsBuilder() |
skyostil | 25c2c012 | 2016-06-09 12:54:12 | [diff] [blame] | 63 | .SetInitialURL(embedded_test_server()->GetURL("/hello.html")) |
| 64 | .Build(); |
skyostil | 585fa60 | 2016-04-25 16:07:56 | [diff] [blame] | 65 | EXPECT_TRUE(WaitForLoad(web_contents)); |
altimin | b6d2d1fc | 2016-04-21 15:23:22 | [diff] [blame] | 66 | |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 67 | EXPECT_THAT(browser_context->GetAllWebContents(), |
| 68 | UnorderedElementsAre(web_contents)); |
altimin | b6d2d1fc | 2016-04-21 15:23:22 | [diff] [blame] | 69 | } |
| 70 | |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 71 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, WindowOpen) { |
altimin | b6d2d1fc | 2016-04-21 15:23:22 | [diff] [blame] | 72 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 73 | |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 74 | HeadlessBrowserContext* browser_context = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 75 | browser()->CreateBrowserContextBuilder().Build(); |
| 76 | |
skyostil | 25c2c012 | 2016-06-09 12:54:12 | [diff] [blame] | 77 | HeadlessWebContents* web_contents = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 78 | browser_context->CreateWebContentsBuilder() |
skyostil | 25c2c012 | 2016-06-09 12:54:12 | [diff] [blame] | 79 | .SetInitialURL(embedded_test_server()->GetURL("/window_open.html")) |
| 80 | .Build(); |
skyostil | 585fa60 | 2016-04-25 16:07:56 | [diff] [blame] | 81 | EXPECT_TRUE(WaitForLoad(web_contents)); |
altimin | b6d2d1fc | 2016-04-21 15:23:22 | [diff] [blame] | 82 | |
Eric Seckler | 48f8a07 | 2017-06-20 14:28:36 | [diff] [blame] | 83 | EXPECT_EQ(2u, browser_context->GetAllWebContents().size()); |
| 84 | |
Johannes Henkel | ed0e5da | 2018-05-30 22:55:18 | [diff] [blame] | 85 | 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 Seckler | 48f8a07 | 2017-06-20 14:28:36 | [diff] [blame] | 95 | 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 Seckler | 82ad0e9 | 2017-07-17 15:52:22 | [diff] [blame] | 102 | |
| 103 | gfx::Rect expected_bounds(0, 0, 200, 100); |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 104 | #if !BUILDFLAG(IS_MAC) |
Eric Seckler | 82ad0e9 | 2017-07-17 15:52:22 | [diff] [blame] | 105 | EXPECT_EQ(expected_bounds, child->web_contents()->GetViewBounds()); |
| 106 | EXPECT_EQ(expected_bounds, child->web_contents()->GetContainerBounds()); |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 107 | #else // !BUILDFLAG(IS_MAC) |
Eric Seckler | 82ad0e9 | 2017-07-17 15:52:22 | [diff] [blame] | 108 | // Mac does not support GetViewBounds() and view positions are random. |
| 109 | EXPECT_EQ(expected_bounds.size(), |
| 110 | child->web_contents()->GetContainerBounds().size()); |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 111 | #endif // !BUILDFLAG(IS_MAC) |
Eric Seckler | 48f8a07 | 2017-06-20 14:28:36 | [diff] [blame] | 112 | } |
| 113 | |
Johannes Henkel | 03212fe | 2018-04-25 17:34:32 | [diff] [blame] | 114 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 115 | FocusOfHeadlessWebContents_IsIndependent) { |
irisu | 559af091 | 2017-02-28 13:10:34 | [diff] [blame] | 116 | 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(); |
arthursonzogni | b3a06c1b | 2018-05-14 11:52:35 | [diff] [blame] | 125 | WaitForLoadAndGainFocus(web_contents); |
irisu | 559af091 | 2017-02-28 13:10:34 | [diff] [blame] | 126 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 127 | EXPECT_THAT(EvaluateScript(web_contents, "document.hasFocus()"), |
| 128 | DictHasValue("result.result.value", true)); |
irisu | 559af091 | 2017-02-28 13:10:34 | [diff] [blame] | 129 | |
| 130 | HeadlessWebContents* web_contents2 = |
| 131 | browser_context->CreateWebContentsBuilder() |
| 132 | .SetInitialURL(embedded_test_server()->GetURL("/hello.html")) |
| 133 | .Build(); |
arthursonzogni | b3a06c1b | 2018-05-14 11:52:35 | [diff] [blame] | 134 | WaitForLoadAndGainFocus(web_contents2); |
irisu | 559af091 | 2017-02-28 13:10:34 | [diff] [blame] | 135 | |
eseckler | c09e399 | 2017-04-03 13:10:36 | [diff] [blame] | 136 | // Focus of different WebContents is independent. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 137 | 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)); |
irisu | 559af091 | 2017-02-28 13:10:34 | [diff] [blame] | 141 | } |
| 142 | |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 143 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, HandleSSLError) { |
sushkov | fd9f294 | 2017-05-09 04:34:36 | [diff] [blame] | 144 | 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 | |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 159 | namespace { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 160 | bool 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); |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 164 | } |
| 165 | } // namespace |
| 166 | |
| 167 | // Parameter specifies whether --disable-gpu should be used. |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 168 | class HeadlessWebContentsScreenshotTest |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 169 | : public HeadlessDevTooledBrowserTest, |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 170 | public ::testing::WithParamInterface<bool> { |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 171 | public: |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 172 | void SetUp() override { |
| 173 | EnablePixelOutput(); |
Sami Kyostila | d99a966 | 2017-06-06 09:09:17 | [diff] [blame] | 174 | if (GetParam()) { |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 175 | UseSoftwareCompositing(); |
Sami Kyostila | d99a966 | 2017-06-06 09:09:17 | [diff] [blame] | 176 | SetUpWithoutGPU(); |
| 177 | } else { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 178 | HeadlessDevTooledBrowserTest::SetUp(); |
Sami Kyostila | d99a966 | 2017-06-06 09:09:17 | [diff] [blame] | 179 | } |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 180 | } |
| 181 | |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 182 | void RunDevTooledTest() override { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 183 | devtools_client_.SendCommand( |
| 184 | "Runtime.evaluate", |
| 185 | Param("expression", "document.body.style.background = '#0000ff'"), |
tzik | f7adad1a | 2018-03-10 20:31:01 | [diff] [blame] | 186 | base::BindOnce(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted, |
| 187 | base::Unretained(this))); |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 188 | } |
| 189 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 190 | void OnPageSetupCompleted(base::Value::Dict) { |
| 191 | devtools_client_.SendCommand( |
| 192 | "Page.captureScreenshot", |
tzik | f7adad1a | 2018-03-10 20:31:01 | [diff] [blame] | 193 | base::BindOnce(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured, |
| 194 | base::Unretained(this))); |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 195 | } |
| 196 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 197 | 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 Henkel | c58a4aef | 2018-10-29 22:39:53 | [diff] [blame] | 203 | EXPECT_GT(png_data.size(), 0U); |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 204 | |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 205 | SkBitmap result_bitmap; |
Johannes Henkel | c58a4aef | 2018-10-29 22:39:53 | [diff] [blame] | 206 | EXPECT_TRUE(DecodePNG(png_data, &result_bitmap)); |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 207 | |
| 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 Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 213 | |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 214 | FinishAsynchronousTest(); |
| 215 | } |
| 216 | }; |
| 217 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 218 | HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest); |
eseckler | d2869ff | 2017-01-19 14:46:21 | [diff] [blame] | 219 | |
| 220 | // Instantiate test case for both software and gpu compositing modes. |
Victor Costan | 33fe978 | 2019-02-01 16:53:09 | [diff] [blame] | 221 | INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotTests, |
| 222 | HeadlessWebContentsScreenshotTest, |
| 223 | ::testing::Bool()); |
eseckler | 9ef5476 | 2016-06-03 17:28:38 | [diff] [blame] | 224 | |
Eric Seckler | 1cbd8e0 | 2018-04-27 03:17:34 | [diff] [blame] | 225 | // Regression test for crbug.com/832138. |
| 226 | class HeadlessWebContentsScreenshotWindowPositionTest |
| 227 | : public HeadlessWebContentsScreenshotTest { |
| 228 | public: |
| 229 | void RunDevTooledTest() override { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 230 | 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 Seckler | 1cbd8e0 | 2018-04-27 03:17:34 | [diff] [blame] | 240 | base::BindOnce( |
| 241 | &HeadlessWebContentsScreenshotWindowPositionTest::OnWindowBoundsSet, |
| 242 | base::Unretained(this))); |
| 243 | } |
| 244 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 245 | void OnWindowBoundsSet(base::Value::Dict result) { |
| 246 | EXPECT_NE(result.FindDict("result"), nullptr); |
Eric Seckler | 1cbd8e0 | 2018-04-27 03:17:34 | [diff] [blame] | 247 | HeadlessWebContentsScreenshotTest::RunDevTooledTest(); |
| 248 | } |
| 249 | }; |
| 250 | |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 251 | #if BUILDFLAG(IS_MAC) && defined(ADDRESS_SANITIZER) |
Jan Wilken Dörrie | db00e482 | 2020-05-27 10:27:08 | [diff] [blame] | 252 | // TODO(crbug.com/1086872): Disabled due to flakiness on Mac ASAN. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 253 | DISABLED_HEADLESS_DEVTOOLED_TEST_P( |
James Cook | 8a55e343 | 2021-03-25 00:08:07 | [diff] [blame] | 254 | HeadlessWebContentsScreenshotWindowPositionTest); |
| 255 | #else |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 256 | HEADLESS_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotWindowPositionTest); |
James Cook | 8a55e343 | 2021-03-25 00:08:07 | [diff] [blame] | 257 | #endif |
| 258 | |
| 259 | // Instantiate test case for both software and gpu compositing modes. |
Victor Costan | 33fe978 | 2019-02-01 16:53:09 | [diff] [blame] | 260 | INSTANTIATE_TEST_SUITE_P(HeadlessWebContentsScreenshotWindowPositionTests, |
| 261 | HeadlessWebContentsScreenshotWindowPositionTest, |
| 262 | ::testing::Bool()); |
Eric Seckler | 1cbd8e0 | 2018-04-27 03:17:34 | [diff] [blame] | 263 | |
Eric Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 264 | // Regression test for https://crbug.com/733569. |
| 265 | class HeadlessWebContentsRequestStorageQuotaTest |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 266 | : public HeadlessDevTooledBrowserTest { |
Eric Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 267 | public: |
| 268 | void RunDevTooledTest() override { |
| 269 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 270 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 271 | devtools_client_.AddEventHandler( |
| 272 | "Runtime.consoleAPICalled", |
| 273 | base::BindRepeating( |
| 274 | &HeadlessWebContentsRequestStorageQuotaTest::OnConsoleAPICalled, |
| 275 | base::Unretained(this))); |
| 276 | SendCommandSync(devtools_client_, "Runtime.enable"); |
Eric Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 277 | |
| 278 | // Should not crash and call console.log() if quota request succeeds. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 279 | devtools_client_.SendCommand( |
| 280 | "Page.navigate", |
| 281 | Param("url", embedded_test_server() |
| 282 | ->GetURL("/request_storage_quota.html") |
| 283 | .spec())); |
Eric Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 284 | } |
| 285 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 286 | 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 Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 295 | FinishAsynchronousTest(); |
| 296 | } |
| 297 | }; |
| 298 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 299 | HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsRequestStorageQuotaTest); |
Eric Seckler | ea51c9c | 2017-07-18 20:47:56 | [diff] [blame] | 300 | |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 301 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserTabChangeContent) { |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 302 | 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 Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 314 | EXPECT_THAT(EvaluateScript(web_contents, script), |
| 315 | Not(DictHasKey("exceptionDetails"))); |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 316 | |
| 317 | // This will time out if the previous script did not work. |
| 318 | EXPECT_TRUE(WaitForLoad(web_contents)); |
| 319 | } |
| 320 | |
Peter Kvitek | 47a3f47 | 2020-11-05 22:47:38 | [diff] [blame] | 321 | IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserOpenInTab) { |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 322 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 323 | |
| 324 | HeadlessBrowserContext* browser_context = |
| 325 | browser()->CreateBrowserContextBuilder().Build(); |
| 326 | |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 327 | 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 Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 339 | EXPECT_THAT(EvaluateScript(web_contents, script), |
| 340 | Not(DictHasKey("exceptionDetails"))); |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 341 | // Check that we have a new tab. |
| 342 | EXPECT_EQ(2u, browser_context->GetAllWebContents().size()); |
Luca Versari | 4128efc | 2017-08-04 13:33:22 | [diff] [blame] | 343 | } |
Alex Clarke | 9155a34 | 2017-09-15 10:56:59 | [diff] [blame] | 344 | |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 345 | // BeginFrameControl is not supported on MacOS. |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 346 | #if !BUILDFLAG(IS_MAC) |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 347 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 348 | // TODO(kvitekp): Check to see if this could be trimmed down by using |
| 349 | // Pre/PostRunAsynchronousTest(). |
| 350 | class HeadlessWebContentsBeginFrameControlTest : public HeadlessBrowserTest { |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 351 | public: |
Eric Seckler | 4948f93 | 2018-08-28 14:51:22 | [diff] [blame] | 352 | HeadlessWebContentsBeginFrameControlTest() {} |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 353 | |
| 354 | void SetUp() override { |
| 355 | EnablePixelOutput(); |
| 356 | HeadlessBrowserTest::SetUp(); |
| 357 | } |
| 358 | |
| 359 | protected: |
| 360 | virtual std::string GetTestHtmlFile() = 0; |
Andrey Kosyakov | 7d50049 | 2022-11-03 23:33:48 | [diff] [blame] | 361 | virtual void StartFrames() {} |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 362 | virtual void OnFrameFinished(base::Value::Dict result) {} |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 363 | |
| 364 | void SetUpCommandLine(base::CommandLine* command_line) override { |
| 365 | HeadlessBrowserTest::SetUpCommandLine(command_line); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 366 | // See bit.ly/headless-rendering for why we use these flags. |
Dominic Mazzoni | 3895ab0 | 2020-01-28 22:50:53 | [diff] [blame] | 367 | command_line->AppendSwitch(::switches::kRunAllCompositorStagesBeforeDraw); |
| 368 | command_line->AppendSwitch(::switches::kDisableNewContentRenderingTimeout); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 369 | command_line->AppendSwitch(cc::switches::kDisableCheckerImaging); |
| 370 | command_line->AppendSwitch(cc::switches::kDisableThreadedAnimation); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 371 | } |
| 372 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 373 | void RunTest() { |
Andrey Kosyakov | ee99924 | 2023-06-30 02:26:26 | [diff] [blame] | 374 | browser()->SetDefaultBrowserContext( |
| 375 | browser()->CreateBrowserContextBuilder().Build()); |
| 376 | SimpleDevToolsProtocolClient browser_devtools_client; |
| 377 | browser_devtools_client.AttachToBrowser(); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 378 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 379 | EXPECT_TRUE(embedded_test_server()->Start()); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 380 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 381 | 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 Kosyakov | ee99924 | 2023-06-30 02:26:26 | [diff] [blame] | 386 | browser_devtools_client.SendCommand( |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 387 | "Target.createTarget", std::move(params), |
tzik | f7adad1a | 2018-03-10 20:31:01 | [diff] [blame] | 388 | base::BindOnce( |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 389 | &HeadlessWebContentsBeginFrameControlTest::OnTargetCreated, |
| 390 | base::Unretained(this))); |
| 391 | |
| 392 | RunAsynchronousTest(); |
| 393 | |
Andrey Kosyakov | ee99924 | 2023-06-30 02:26:26 | [diff] [blame] | 394 | browser_devtools_client.DetachClient(); |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 395 | } |
| 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 Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 405 | 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 Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 413 | } |
| 414 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 415 | void OnPageDomainEnabled(base::Value::Dict) { |
| 416 | devtools_client_.SendCommand( |
| 417 | "Page.navigate", |
| 418 | Param("url", embedded_test_server()->GetURL(GetTestHtmlFile()).spec())); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 419 | } |
| 420 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 421 | void OnLoadEventFired(const base::Value::Dict& params) { |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 422 | TRACE_EVENT0("headless", |
| 423 | "HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired"); |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 424 | |
| 425 | devtools_client_.SendCommand("Page.disable"); |
| 426 | devtools_client_.RemoveEventHandler("Page.loadEventFired", |
| 427 | on_load_event_fired_handler_); |
| 428 | |
Andrey Kosyakov | 7d50049 | 2022-11-03 23:33:48 | [diff] [blame] | 429 | StartFrames(); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 430 | } |
| 431 | |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 432 | void BeginFrame(bool screenshot) { |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 433 | num_begin_frames_++; |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 434 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 435 | base::Value::Dict params; |
| 436 | if (screenshot) |
| 437 | params.Set("screenshot", base::Value::Dict()); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 438 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 439 | devtools_client_.SendCommand( |
| 440 | "HeadlessExperimental.beginFrame", std::move(params), |
tzik | f7adad1a | 2018-03-10 20:31:01 | [diff] [blame] | 441 | base::BindOnce(&HeadlessWebContentsBeginFrameControlTest::FrameFinished, |
| 442 | base::Unretained(this))); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 443 | } |
| 444 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 445 | 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 Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 450 | |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 451 | OnFrameFinished(std::move(result)); |
| 452 | } |
| 453 | |
| 454 | void PostFinishAsynchronousTest() { |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 455 | browser()->BrowserMainThread()->PostTask( |
| 456 | FROM_HERE, |
| 457 | base::BindOnce( |
| 458 | &HeadlessWebContentsBeginFrameControlTest::FinishAsynchronousTest, |
| 459 | base::Unretained(this))); |
| 460 | } |
| 461 | |
Pâris | e6361d0 | 2023-07-19 09:00:43 | [diff] [blame] | 462 | raw_ptr<HeadlessWebContentsImpl, AcrossTasksDanglingUntriaged> web_contents_ = |
Ali Hijazi | 4d4e2409 | 2022-10-20 22:59:31 | [diff] [blame] | 463 | nullptr; // Not owned. |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 464 | |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 465 | int num_begin_frames_ = 0; |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 466 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 467 | SimpleDevToolsProtocolClient devtools_client_; |
| 468 | |
| 469 | SimpleDevToolsProtocolClient::EventCallback on_load_event_fired_handler_ = |
| 470 | base::BindRepeating( |
| 471 | &HeadlessWebContentsBeginFrameControlTest::OnLoadEventFired, |
| 472 | base::Unretained(this)); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 473 | }; |
| 474 | |
| 475 | class HeadlessWebContentsBeginFrameControlBasicTest |
| 476 | : public HeadlessWebContentsBeginFrameControlTest { |
| 477 | public: |
Chris Watkins | 6e89eee | 2017-11-29 06:01:18 | [diff] [blame] | 478 | HeadlessWebContentsBeginFrameControlBasicTest() = default; |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 479 | |
| 480 | protected: |
| 481 | std::string GetTestHtmlFile() override { |
| 482 | // Blue background. |
| 483 | return "/blue_page.html"; |
| 484 | } |
| 485 | |
Andrey Kosyakov | 7d50049 | 2022-11-03 23:33:48 | [diff] [blame] | 486 | void StartFrames() override { BeginFrame(true); } |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 487 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 488 | void OnFrameFinished(base::Value::Dict result) override { |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 489 | if (num_begin_frames_ == 1) { |
Saman Sami | 5c23608 | 2018-10-11 19:15:07 | [diff] [blame] | 490 | // First BeginFrame should have caused damage and have a screenshot. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 491 | 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 Sami | 5c23608 | 2018-10-11 19:15:07 | [diff] [blame] | 500 | SkBitmap result_bitmap; |
Johannes Henkel | c58a4aef | 2018-10-29 22:39:53 | [diff] [blame] | 501 | EXPECT_TRUE(DecodePNG(png_data, &result_bitmap)); |
Saman Sami | 5c23608 | 2018-10-11 19:15:07 | [diff] [blame] | 502 | 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 Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 507 | } else { |
Saman Sami | 5c23608 | 2018-10-11 19:15:07 | [diff] [blame] | 508 | DCHECK_EQ(2, num_begin_frames_); |
| 509 | // Can't guarantee that the second BeginFrame didn't have damage, but it |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 510 | // should not have a screenshot. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 511 | EXPECT_FALSE(result.FindStringByDottedPath("result.screenshotData")); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 512 | } |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 513 | |
Saman Sami | 5c23608 | 2018-10-11 19:15:07 | [diff] [blame] | 514 | if (num_begin_frames_ < 2) { |
| 515 | // Don't capture a screenshot in the second BeginFrame. |
| 516 | BeginFrame(false); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 517 | } else { |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 518 | // Post completion to avoid deleting the WebContents on the same callstack |
| 519 | // as frame finished callback. |
| 520 | PostFinishAsynchronousTest(); |
| 521 | } |
| 522 | } |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 523 | }; |
| 524 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 525 | HEADLESS_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 526 | |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 527 | class HeadlessWebContentsBeginFrameControlViewportTest |
| 528 | : public HeadlessWebContentsBeginFrameControlTest { |
| 529 | public: |
Chris Watkins | 6e89eee | 2017-11-29 06:01:18 | [diff] [blame] | 530 | HeadlessWebContentsBeginFrameControlViewportTest() = default; |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 531 | |
| 532 | protected: |
| 533 | std::string GetTestHtmlFile() override { |
| 534 | // Draws a 100x100px blue box at 200x200px. |
| 535 | return "/blue_box.html"; |
| 536 | } |
| 537 | |
Andrey Kosyakov | 7d50049 | 2022-11-03 23:33:48 | [diff] [blame] | 538 | void StartFrames() override { |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 539 | // Send a first BeginFrame to initialize the surface. |
| 540 | BeginFrame(false); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 541 | } |
| 542 | |
| 543 | void SetUpViewport() { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 544 | 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 Seckler | 7b9e995 | 2017-11-14 19:08:31 | [diff] [blame] | 560 | } |
| 561 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 562 | void OnSetDeviceMetricsOverrideDone(base::Value::Dict result) { |
| 563 | EXPECT_THAT(result, DictHasKey("result")); |
Saman Sami | 33be507c | 2018-03-02 22:33:08 | [diff] [blame] | 564 | // Take a screenshot in the second BeginFrame. |
| 565 | BeginFrame(true); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 566 | } |
| 567 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 568 | void OnFrameFinished(base::Value::Dict result) override { |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 569 | if (num_begin_frames_ == 1) { |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 570 | SetUpViewport(); |
| 571 | return; |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 572 | } |
| 573 | |
Saman Sami | 33be507c | 2018-03-02 22:33:08 | [diff] [blame] | 574 | DCHECK_EQ(2, num_begin_frames_); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 575 | // Second BeginFrame should have a screenshot of the configured viewport and |
| 576 | // of the correct size. |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 577 | EXPECT_TRUE(*result.FindBoolByDottedPath("result.hasDamage")); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 578 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 579 | std::string png_data_base64 = DictString(result, "result.screenshotData"); |
| 580 | ASSERT_FALSE(png_data_base64.empty()); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 581 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 582 | 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 Wang | 43fe444 | 2023-01-24 23:59:09 | [diff] [blame] | 596 | EXPECT_TRUE(cc::MatchesBitmap(result_bitmap, expected_bitmap, |
| 597 | cc::ExactPixelComparator())); |
Eric Seckler | 4f0eb9fa | 2018-03-01 11:23:21 | [diff] [blame] | 598 | |
| 599 | // Post completion to avoid deleting the WebContents on the same callstack |
| 600 | // as frame finished callback. |
| 601 | PostFinishAsynchronousTest(); |
| 602 | } |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 603 | }; |
| 604 | |
Frank Liberato | 93ec02b | 2023-06-29 23:20:33 | [diff] [blame] | 605 | // TODO(crbug.com/1459385): Turning this off since it's flaking regularly. |
| 606 | DISABLED_HEADLESS_DEVTOOLED_TEST_F( |
| 607 | HeadlessWebContentsBeginFrameControlViewportTest); |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 608 | |
Xiaohan Wang | 2d5a55e | 2022-01-08 01:34:16 | [diff] [blame] | 609 | #endif // !BUILDFLAG(IS_MAC) |
Eric Seckler | 80a868a | 2017-10-10 22:57:44 | [diff] [blame] | 610 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 611 | class CookiesEnabled : public HeadlessDevTooledBrowserTest { |
Alex Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 612 | public: |
| 613 | void RunDevTooledTest() override { |
Alex Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 614 | EXPECT_TRUE(embedded_test_server()->Start()); |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 615 | |
| 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 Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 625 | } |
| 626 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 627 | 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 Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 632 | } |
| 633 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 634 | void OnEvaluateResult(base::Value::Dict result) { |
| 635 | EXPECT_EQ(DictString(result, "result.result.value"), "0"); |
| 636 | |
Alex Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 637 | FinishAsynchronousTest(); |
| 638 | } |
Alex Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 639 | }; |
| 640 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 641 | HEADLESS_DEVTOOLED_TEST_F(CookiesEnabled); |
Alex Clarke | 61c3cd7 | 2017-10-17 09:31:22 | [diff] [blame] | 642 | |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 643 | namespace { |
| 644 | const char* kPageWhichOpensAWindow = R"( |
| 645 | <html> |
| 646 | <body> |
| 647 | <script> |
Andrey Kosyakov | 5ef2021 | 2020-09-17 21:44:17 | [diff] [blame] | 648 | const win = window.open('/page2.html'); |
| 649 | if (!win) |
| 650 | console.error('ready'); |
| 651 | win.addEventListener('load', () => console.log('ready')); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 652 | </script> |
| 653 | </body> |
| 654 | </html> |
| 655 | )"; |
| 656 | |
| 657 | const char* kPage2 = R"( |
| 658 | <html> |
| 659 | <body> |
| 660 | Page 2. |
| 661 | </body> |
| 662 | </html> |
| 663 | )"; |
| 664 | } // namespace |
| 665 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 666 | class WebContentsOpenTest : public HeadlessDevTooledBrowserTest { |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 667 | public: |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 668 | void PreRunAsynchronousTest() override { |
| 669 | interceptor_ = std::make_unique<TestNetworkInterceptor>(); |
| 670 | } |
| 671 | |
| 672 | void PostRunAsynchronousTest() override { interceptor_.reset(); } |
| 673 | |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 674 | void RunDevTooledTest() override { |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 675 | DCHECK(interceptor_); |
| 676 | |
Andrey Kosyakov | 8846844e | 2018-07-10 00:49:22 | [diff] [blame] | 677 | interceptor_->InsertResponse("http://foo.com/index.html", |
| 678 | {kPageWhichOpensAWindow, "text/html"}); |
| 679 | interceptor_->InsertResponse("http://foo.com/page2.html", |
| 680 | {kPage2, "text/html"}); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 681 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 682 | devtools_client_.AddEventHandler( |
| 683 | "Runtime.consoleAPICalled", |
| 684 | base::BindRepeating(&WebContentsOpenTest::OnConsoleAPICalled, |
| 685 | base::Unretained(this))); |
| 686 | SendCommandSync(devtools_client_, "Runtime.enable"); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 687 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 688 | devtools_client_.SendCommand("Page.navigate", |
| 689 | Param("url", "http://foo.com/index.html")); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 690 | } |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 691 | |
| 692 | virtual void OnConsoleAPICalled(const base::Value::Dict& params) {} |
| 693 | |
| 694 | protected: |
| 695 | std::unique_ptr<TestNetworkInterceptor> interceptor_; |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 696 | }; |
| 697 | |
| 698 | class DontBlockWebContentsOpenTest : public WebContentsOpenTest { |
| 699 | public: |
| 700 | void CustomizeHeadlessBrowserContext( |
| 701 | HeadlessBrowserContext::Builder& builder) override { |
| 702 | builder.SetBlockNewWebContents(false); |
| 703 | } |
| 704 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 705 | void OnConsoleAPICalled(const base::Value::Dict& params) override { |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 706 | EXPECT_THAT( |
Andrey Kosyakov | 8846844e | 2018-07-10 00:49:22 | [diff] [blame] | 707 | interceptor_->urls_requested(), |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 708 | ElementsAre("http://foo.com/index.html", "http://foo.com/page2.html")); |
| 709 | FinishAsynchronousTest(); |
| 710 | } |
| 711 | }; |
| 712 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 713 | HEADLESS_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 714 | |
| 715 | class BlockWebContentsOpenTest : public WebContentsOpenTest { |
| 716 | public: |
| 717 | void CustomizeHeadlessBrowserContext( |
| 718 | HeadlessBrowserContext::Builder& builder) override { |
| 719 | builder.SetBlockNewWebContents(true); |
| 720 | } |
| 721 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 722 | void OnConsoleAPICalled(const base::Value::Dict& params) override { |
Andrey Kosyakov | 8846844e | 2018-07-10 00:49:22 | [diff] [blame] | 723 | EXPECT_THAT(interceptor_->urls_requested(), |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 724 | ElementsAre("http://foo.com/index.html")); |
| 725 | FinishAsynchronousTest(); |
| 726 | } |
| 727 | }; |
| 728 | |
Peter Kvitek | c090ae6 | 2022-11-02 23:17:39 | [diff] [blame] | 729 | HEADLESS_DEVTOOLED_TEST_F(BlockWebContentsOpenTest); |
Alex Clarke | 6bd0fd5 | 2018-02-12 16:08:25 | [diff] [blame] | 730 | |
Sami Kyostila | 5606dfd | 2022-11-30 02:47:01 | [diff] [blame] | 731 | // Regression test for crbug.com/1385982. |
| 732 | class BlockDevToolsEmbedding : public HeadlessDevTooledBrowserTest { |
| 733 | protected: |
Andrey Kosyakov | 9bfac97 | 2023-01-18 18:16:50 | [diff] [blame] | 734 | void SetUpCommandLine(base::CommandLine* command_line) override { |
| 735 | HeadlessDevTooledBrowserTest::SetUpCommandLine(command_line); |
| 736 | command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, |
| 737 | base::NumberToString(port_)); |
Sami Kyostila | 5606dfd | 2022-11-30 02:47:01 | [diff] [blame] | 738 | } |
| 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 | |
| 773 | HEADLESS_DEVTOOLED_TEST_F(BlockDevToolsEmbedding); |
| 774 | |
skyostil | fe11616 | 2016-02-26 20:53:33 | [diff] [blame] | 775 | } // namespace headless |