| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "content/public/test/browser_test.h" |
| #include "headless/public/devtools/domains/page.h" |
| #include "headless/public/devtools/domains/runtime.h" |
| #include "headless/public/devtools/domains/security.h" |
| #include "headless/public/headless_browser.h" |
| #include "headless/public/headless_devtools_client.h" |
| #include "headless/public/headless_tab_socket.h" |
| #include "headless/public/headless_web_contents.h" |
| #include "headless/test/headless_browser_test.h" |
| #include "printing/features/features.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.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 "url/gurl.h" |
| |
| #if BUILDFLAG(ENABLE_BASIC_PRINTING) |
| #include "base/strings/string_number_conversions.h" |
| #include "pdf/pdf.h" |
| #include "printing/pdf_render_settings.h" |
| #include "printing/units.h" |
| #include "ui/gfx/geometry/rect.h" |
| #endif |
| |
| using testing::UnorderedElementsAre; |
| |
| 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(static_cast<size_t>(2), |
| browser_context->GetAllWebContents().size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Focus) { |
| 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)); |
| |
| bool result; |
| EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()") |
| ->GetResult() |
| ->GetValue() |
| ->GetAsBoolean(&result)); |
| EXPECT_TRUE(result); |
| |
| HeadlessWebContents* web_contents2 = |
| browser_context->CreateWebContentsBuilder() |
| .SetInitialURL(embedded_test_server()->GetURL("/hello.html")) |
| .Build(); |
| EXPECT_TRUE(WaitForLoad(web_contents2)); |
| |
| // Focus of different WebContents is independent. |
| EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()") |
| ->GetResult() |
| ->GetValue() |
| ->GetAsBoolean(&result)); |
| EXPECT_TRUE(result); |
| EXPECT_TRUE(EvaluateScript(web_contents2, "document.hasFocus()") |
| ->GetResult() |
| ->GetValue() |
| ->GetAsBoolean(&result)); |
| EXPECT_TRUE(result); |
| } |
| |
| 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(std::string base64_data, SkBitmap* bitmap) { |
| std::string png_data; |
| if (!base::Base64Decode(base64_data, &png_data)) |
| return false; |
| return gfx::PNGCodec::Decode( |
| reinterpret_cast<unsigned const char*>(png_data.data()), png_data.size(), |
| bitmap); |
| } |
| } // namespace |
| |
| // Parameter specifies whether --disable-gpu should be used. |
| class HeadlessWebContentsScreenshotTest |
| : public HeadlessAsyncDevTooledBrowserTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| void SetUp() override { |
| EnablePixelOutput(); |
| if (GetParam()) |
| UseSoftwareCompositing(); |
| HeadlessAsyncDevTooledBrowserTest::SetUp(); |
| } |
| |
| void RunDevTooledTest() override { |
| std::unique_ptr<runtime::EvaluateParams> params = |
| runtime::EvaluateParams::Builder() |
| .SetExpression("document.body.style.background = '#0000ff'") |
| .Build(); |
| devtools_client_->GetRuntime()->Evaluate( |
| std::move(params), |
| base::Bind(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted, |
| base::Unretained(this))); |
| } |
| |
| void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) { |
| devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( |
| page::CaptureScreenshotParams::Builder().SetFromSurface(true).Build(), |
| base::Bind(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured, |
| base::Unretained(this))); |
| } |
| |
| void OnScreenshotCaptured( |
| std::unique_ptr<page::CaptureScreenshotResult> result) { |
| std::string base64 = result->GetData(); |
| EXPECT_GT(base64.length(), 0U); |
| SkBitmap result_bitmap; |
| EXPECT_TRUE(DecodePNG(base64, &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_ASYNC_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest); |
| |
| // Instantiate test case for both software and gpu compositing modes. |
| INSTANTIATE_TEST_CASE_P(HeadlessWebContentsScreenshotTests, |
| HeadlessWebContentsScreenshotTest, |
| ::testing::Bool()); |
| |
| #if BUILDFLAG(ENABLE_BASIC_PRINTING) |
| class HeadlessWebContentsPDFTest : public HeadlessAsyncDevTooledBrowserTest { |
| public: |
| const double kPaperWidth = 10; |
| const double kPaperHeight = 15; |
| const double kDocHeight = 50; |
| // Number of color channels in a BGRA bitmap. |
| const int kColorChannels = 4; |
| const int kDpi = 300; |
| |
| void RunDevTooledTest() override { |
| std::string height_expression = "document.body.style.height = '" + |
| base::DoubleToString(kDocHeight) + "in'"; |
| std::unique_ptr<runtime::EvaluateParams> params = |
| runtime::EvaluateParams::Builder() |
| .SetExpression("document.body.style.background = '#123456';" + |
| height_expression) |
| .Build(); |
| devtools_client_->GetRuntime()->Evaluate( |
| std::move(params), |
| base::Bind(&HeadlessWebContentsPDFTest::OnPageSetupCompleted, |
| base::Unretained(this))); |
| } |
| |
| void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) { |
| devtools_client_->GetPage()->GetExperimental()->PrintToPDF( |
| page::PrintToPDFParams::Builder() |
| .SetPrintBackground(true) |
| .SetPaperHeight(kPaperHeight) |
| .SetPaperWidth(kPaperWidth) |
| .SetMarginTop(0) |
| .SetMarginBottom(0) |
| .SetMarginLeft(0) |
| .SetMarginRight(0) |
| .Build(), |
| base::Bind(&HeadlessWebContentsPDFTest::OnPDFCreated, |
| base::Unretained(this))); |
| } |
| |
| void OnPDFCreated(std::unique_ptr<page::PrintToPDFResult> result) { |
| std::string base64 = result->GetData(); |
| EXPECT_GT(base64.length(), 0U); |
| std::string pdf_data; |
| EXPECT_TRUE(base::Base64Decode(base64, &pdf_data)); |
| |
| int num_pages; |
| EXPECT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_data.data(), pdf_data.size(), |
| &num_pages, nullptr)); |
| EXPECT_EQ(std::ceil(kDocHeight / kPaperHeight), num_pages); |
| |
| for (int i = 0; i < num_pages; i++) { |
| double width_in_points; |
| double height_in_points; |
| EXPECT_TRUE(chrome_pdf::GetPDFPageSizeByIndex( |
| pdf_data.data(), pdf_data.size(), i, &width_in_points, |
| &height_in_points)); |
| EXPECT_EQ(static_cast<int>(width_in_points), |
| static_cast<int>(kPaperWidth * printing::kPointsPerInch)); |
| EXPECT_EQ(static_cast<int>(height_in_points), |
| static_cast<int>(kPaperHeight * printing::kPointsPerInch)); |
| |
| gfx::Rect rect(kPaperWidth * kDpi, kPaperHeight * kDpi); |
| printing::PdfRenderSettings settings( |
| rect, gfx::Point(0, 0), kDpi, true, |
| printing::PdfRenderSettings::Mode::NORMAL); |
| std::vector<uint8_t> page_bitmap_data(kColorChannels * |
| settings.area.size().GetArea()); |
| EXPECT_TRUE(chrome_pdf::RenderPDFPageToBitmap( |
| pdf_data.data(), pdf_data.size(), i, page_bitmap_data.data(), |
| settings.area.size().width(), settings.area.size().height(), |
| settings.dpi, settings.autorotate)); |
| EXPECT_EQ(0x56, page_bitmap_data[0]); // B |
| EXPECT_EQ(0x34, page_bitmap_data[1]); // G |
| EXPECT_EQ(0x12, page_bitmap_data[2]); // R |
| } |
| FinishAsynchronousTest(); |
| } |
| }; |
| |
| HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsPDFTest); |
| #endif |
| |
| class HeadlessWebContentsSecurityTest |
| : public HeadlessAsyncDevTooledBrowserTest, |
| public security::ExperimentalObserver { |
| public: |
| void RunDevTooledTest() override { |
| devtools_client_->GetSecurity()->GetExperimental()->AddObserver(this); |
| devtools_client_->GetSecurity()->GetExperimental()->Enable( |
| security::EnableParams::Builder().Build()); |
| } |
| |
| void OnSecurityStateChanged( |
| const security::SecurityStateChangedParams& params) override { |
| EXPECT_EQ(security::SecurityState::NEUTRAL, params.GetSecurityState()); |
| |
| devtools_client_->GetSecurity()->GetExperimental()->Disable( |
| security::DisableParams::Builder().Build()); |
| devtools_client_->GetSecurity()->GetExperimental()->RemoveObserver(this); |
| FinishAsynchronousTest(); |
| } |
| }; |
| |
| HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsSecurityTest); |
| |
| class HeadlessTabSocketTest : public HeadlessAsyncDevTooledBrowserTest, |
| public HeadlessTabSocket::Listener { |
| public: |
| void SetUp() override { |
| options()->mojo_service_names.insert("headless::TabSocket"); |
| HeadlessAsyncDevTooledBrowserTest::SetUp(); |
| } |
| |
| void RunDevTooledTest() override { |
| devtools_client_->GetRuntime()->Evaluate( |
| R"(window.TabSocket.onmessage = |
| function(event) { |
| window.TabSocket.send( |
| 'Embedder sent us: ' + event.detail.message); |
| }; |
| )"); |
| |
| HeadlessTabSocket* headless_tab_socket = |
| web_contents_->GetHeadlessTabSocket(); |
| DCHECK(headless_tab_socket); |
| |
| headless_tab_socket->SendMessageToTab("Hello"); |
| headless_tab_socket->SetListener(this); |
| } |
| |
| void OnMessageFromTab(const std::string& message) override { |
| EXPECT_EQ("Embedder sent us: Hello", message); |
| FinishAsynchronousTest(); |
| } |
| |
| bool GetCreateTabSocket() override { return true; } |
| }; |
| |
| HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessTabSocketTest); |
| |
| } // namespace headless |