| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "headless/test/headless_devtooled_browsertest.h" |
| |
| #include <memory> |
| |
| #include "base/check_deref.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test.h" |
| #include "headless/lib/browser/headless_web_contents_impl.h" |
| #include "headless/public/headless_browser_context.h" |
| #include "headless/test/headless_browser_test_utils.h" |
| |
| namespace headless { |
| |
| HeadlessDevTooledBrowserTest::HeadlessDevTooledBrowserTest() = default; |
| HeadlessDevTooledBrowserTest::~HeadlessDevTooledBrowserTest() = default; |
| |
| void HeadlessDevTooledBrowserTest::RunTest() { |
| HeadlessBrowserContext::Builder builder = |
| browser()->CreateBrowserContextBuilder(); |
| CustomizeHeadlessBrowserContext(builder); |
| browser_context_ = builder.Build(); |
| browser()->SetDefaultBrowserContext(browser_context_); |
| |
| browser_devtools_client_.AttachToBrowser(); |
| |
| HeadlessWebContents::Builder web_contents_builder = |
| browser_context_->CreateWebContentsBuilder(); |
| web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl()); |
| CustomizeHeadlessWebContents(web_contents_builder); |
| web_contents_ = web_contents_builder.Build(); |
| Observe(HeadlessWebContentsImpl::From(web_contents_)->web_contents()); |
| |
| PreRunAsynchronousTest(); |
| RunAsynchronousTest(); |
| PostRunAsynchronousTest(); |
| |
| devtools_client_.DetachClient(); |
| Observe(nullptr); |
| { |
| // Keep raw_ptr<> happy and clear it before WC dies. |
| HeadlessWebContents& wc = *web_contents_; |
| web_contents_ = nullptr; |
| wc.Close(); |
| } |
| { |
| HeadlessBrowserContext& bc = *browser_context_; |
| browser_context_ = nullptr; |
| browser_devtools_client_.DetachClient(); |
| bc.Close(); |
| } |
| |
| // Let the tasks that might have beein scheduled during web contents |
| // being closed run (see https://crbug.com/1036627 for details). |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void HeadlessDevTooledBrowserTest::RenderViewReady() { |
| if (had_render_view_ready_) { |
| return; |
| } |
| had_render_view_ready_ = true; |
| |
| CHECK(HeadlessWebContentsImpl::From(web_contents_) |
| ->web_contents() |
| ->GetPrimaryMainFrame() |
| ->IsRenderFrameLive()); |
| |
| DevToolsTargetReady(); |
| |
| devtools_client_.AttachToWebContents( |
| HeadlessWebContentsImpl::From(web_contents_)->web_contents()); |
| |
| RunDevTooledTest(); |
| } |
| |
| void HeadlessDevTooledBrowserTest::PrimaryMainFrameRenderProcessGone( |
| base::TerminationStatus status) { |
| if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION) |
| return; |
| |
| FinishAsynchronousTest(); |
| FAIL() << "Abnormal renderer termination (status=" << status << ")"; |
| } |
| |
| bool HeadlessDevTooledBrowserTest::GetEnableBeginFrameControl() { |
| return false; |
| } |
| |
| void HeadlessDevTooledBrowserTest::CustomizeHeadlessBrowserContext( |
| HeadlessBrowserContext::Builder& builder) {} |
| |
| void HeadlessDevTooledBrowserTest::CustomizeHeadlessWebContents( |
| HeadlessWebContents::Builder& builder) {} |
| |
| // DevTooled browser tests --------------------------------------------------- |
| |
| // This test was implicitly disabled on Fuchsia while being part of headless |
| // protocol tests before it was moved here. |
| // TODO(crbug.com/40222911): Enable on Fuchsia when no longer flakily timeout. |
| #if !BUILDFLAG(IS_FUCHSIA) |
| |
| class HeadlessAllowedVideoCodecsTest |
| : public HeadlessDevTooledBrowserTest, |
| public testing::WithParamInterface< |
| std::tuple<std::string, std::string, bool>> { |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| HeadlessDevTooledBrowserTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitchASCII("allow-video-codecs", allowlist()); |
| } |
| |
| void RunDevTooledTest() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| SendCommandSync(devtools_client_, "Page.enable"); |
| devtools_client_.AddEventHandler( |
| "Page.loadEventFired", |
| base::BindRepeating(&HeadlessAllowedVideoCodecsTest::OnLoadEventFired, |
| base::Unretained(this))); |
| devtools_client_.SendCommand( |
| "Page.navigate", |
| Param("url", embedded_test_server()->GetURL("/hello.html").spec())); |
| } |
| |
| void OnLoadEventFired(const base::DictValue& params) { |
| base::DictValue eval_params; |
| eval_params.Set("returnByValue", true); |
| eval_params.Set("awaitPromise", true); |
| eval_params.Set("expression", base::StringPrintf(R"( |
| VideoDecoder.isConfigSupported({codec: "%s"}) |
| .then(result => result.supported) |
| )", |
| codec_name().c_str())); |
| base::DictValue result = SendCommandSync( |
| devtools_client_, "Runtime.evaluate", std::move(eval_params)); |
| EXPECT_THAT(result.FindBoolByDottedPath("result.result.value"), |
| testing::Optional(is_codec_enabled())); |
| FinishAsynchronousTest(); |
| } |
| |
| const std::string& allowlist() const { return std::get<0>(GetParam()); } |
| const std::string& codec_name() const { return std::get<1>(GetParam()); } |
| bool is_codec_enabled() const { return std::get<2>(GetParam()); } |
| }; |
| |
| constexpr bool have_proprietary_codecs = |
| #if BUILDFLAG(USE_PROPRIETARY_CODECS) |
| true; |
| #else |
| false; |
| #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) |
| |
| INSTANTIATE_TEST_SUITE_P( |
| /* no prefix */, |
| HeadlessAllowedVideoCodecsTest, |
| testing::Values( |
| std::make_tuple("av1,-*", "av01.0.04M.08", true), |
| std::make_tuple("-av1,*", "av01.0.04M.08", false), |
| std::make_tuple("*", "avc1.64000b", have_proprietary_codecs))); |
| |
| HEADLESS_DEVTOOLED_TEST_P(HeadlessAllowedVideoCodecsTest); |
| |
| #endif // #if !BUILDFLAG(IS_FUCHSIA) |
| |
| class HeadlessCreatedTargetIsUsedProcessTest |
| : public HeadlessDevTooledBrowserTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| HeadlessCreatedTargetIsUsedProcessTest() = default; |
| |
| bool IsHiddenTarget() const { return GetParam(); } |
| |
| private: |
| void RunDevTooledTest() override { |
| base::DictValue params; |
| params.Set("url", ""); |
| params.Set("hidden", IsHiddenTarget()); |
| browser_devtools_client_.SendCommand( |
| "Target.createTarget", std::move(params), |
| base::BindOnce(&HeadlessCreatedTargetIsUsedProcessTest::OnTargetCreated, |
| base::Unretained(this))); |
| } |
| |
| void OnTargetCreated(base::DictValue result) { |
| std::string target_id = DictString(result, "result.targetId"); |
| ASSERT_FALSE(target_id.empty()); |
| |
| scoped_refptr<content::DevToolsAgentHost> agent_host = |
| content::DevToolsAgentHost::GetForId(target_id); |
| ASSERT_TRUE(agent_host); |
| |
| content::WebContents* web_contents = agent_host->GetWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| bool is_used = |
| !web_contents->GetPrimaryMainFrame()->GetProcess()->IsUnused(); |
| EXPECT_EQ(is_used, IsHiddenTarget()); |
| |
| FinishAsynchronousTest(); |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| /* no prefix */, |
| HeadlessCreatedTargetIsUsedProcessTest, |
| testing::Bool(), |
| [](const testing::TestParamInfo<bool>& info) { |
| return info.param ? "hidden" : "normal"; |
| }); |
| |
| HEADLESS_DEVTOOLED_TEST_P(HeadlessCreatedTargetIsUsedProcessTest); |
| |
| } // namespace headless |