blob: 77972b160c3566ee54328fbd35a04ce250099002 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "components/headless/policy/headless_mode_policy.h"
#include "components/headless/test/capture_std_stream.h"
#include "components/policy/core/browser/browser_policy_connector_base.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "headless/lib/browser/headless_browser_impl.h"
#include "headless/public/headless_browser.h"
#include "headless/public/switches.h"
#include "headless/test/headless_browser_test.h"
#include "headless/test/headless_browser_test_utils.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !BUILDFLAG(IS_WIN)
#include <unistd.h>
#endif
namespace headless {
// The following enum values must match HeadlessMode policy template in
// components/policy/resources/templates/policy_definitions/Miscellaneous/HeadlessMode.yaml
enum {
kHeadlessModePolicyEnabled = 1,
kHeadlessModePolicyDisabled = 2,
kHeadlessModePolicyUnset = -1, // not in the template
};
class HeadlessBrowserTestWithPolicy : public HeadlessBrowserTest {
protected:
// Implement to set policies before headless browser is instantiated.
virtual void SetPolicy() {}
void SetUp() override {
mock_provider_ = std::make_unique<
testing::NiceMock<policy::MockConfigurationPolicyProvider>>();
mock_provider_->SetDefaultReturns(
/*is_initialization_complete_return=*/false,
/*is_first_policy_load_complete_return=*/false);
policy::BrowserPolicyConnectorBase::SetPolicyProviderForTesting(
mock_provider_.get());
SetPolicy();
HeadlessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
command_line->AppendSwitchPath(switches::kUserDataDir,
user_data_dir_.GetPath());
}
void TearDown() override {
HeadlessBrowserTest::TearDown();
mock_provider_->Shutdown();
policy::BrowserPolicyConnectorBase::SetPolicyProviderForTesting(nullptr);
}
PrefService* GetPrefs() {
return static_cast<HeadlessBrowserImpl*>(browser())->GetPrefs();
}
base::ScopedTempDir user_data_dir_;
std::unique_ptr<policy::MockConfigurationPolicyProvider> mock_provider_;
};
class HeadlessBrowserTestWithHeadlessModePolicy
: public HeadlessBrowserTestWithPolicy,
public testing::WithParamInterface<std::tuple<int, bool>> {
protected:
void SetPolicy() override {
int headless_mode_policy = std::get<0>(GetParam());
if (headless_mode_policy != kHeadlessModePolicyUnset) {
SetHeadlessModePolicy(
static_cast<HeadlessModePolicy::HeadlessMode>(headless_mode_policy));
}
}
void SetHeadlessModePolicy(HeadlessModePolicy::HeadlessMode headless_mode) {
policy::PolicyMap policy;
policy.Set("HeadlessMode", policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(static_cast<int>(headless_mode)),
/*external_data_fetcher=*/nullptr);
mock_provider_->UpdateChromePolicy(policy);
}
bool expected_enabled() { return std::get<1>(GetParam()); }
bool actual_enabled() {
return !HeadlessModePolicy::IsHeadlessModeDisabled(GetPrefs());
}
};
INSTANTIATE_TEST_SUITE_P(
HeadlessBrowserTestWithHeadlessModePolicy,
HeadlessBrowserTestWithHeadlessModePolicy,
testing::Values(std::make_tuple(kHeadlessModePolicyEnabled, true),
std::make_tuple(kHeadlessModePolicyDisabled, false),
std::make_tuple(kHeadlessModePolicyUnset, true)));
IN_PROC_BROWSER_TEST_P(HeadlessBrowserTestWithHeadlessModePolicy,
HeadlessModePolicySettings) {
EXPECT_EQ(actual_enabled(), expected_enabled());
}
class HeadlessBrowserTestWithUrlBlockPolicy
: public HeadlessBrowserTestWithPolicy {
protected:
void SetPolicy() override {
base::Value::List value;
value.Append("*/blocked.html");
policy::PolicyMap policy;
policy.Set("URLBlocklist", policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(std::move(value)),
/*external_data_fetcher=*/nullptr);
mock_provider_->UpdateChromePolicy(policy);
}
};
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTestWithUrlBlockPolicy, BlockUrl) {
EXPECT_TRUE(embedded_test_server()->Start());
HeadlessBrowserContext* browser_context =
browser()->CreateBrowserContextBuilder().Build();
GURL url = embedded_test_server()->GetURL("/blocked.html");
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().SetInitialURL(url).Build();
net::Error error = net::OK;
EXPECT_FALSE(WaitForLoad(web_contents, &error));
EXPECT_EQ(error, net::ERR_BLOCKED_BY_ADMINISTRATOR);
}
class HeadlessBrowserTestWithRemoteDebuggingAllowedPolicy
: public HeadlessBrowserTestWithPolicy,
public testing::WithParamInterface<bool> {
protected:
void SetPolicy() override {
policy::PolicyMap policy;
policy.Set("RemoteDebuggingAllowed", policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
base::Value(expect_remote_debugging_available()),
/*external_data_fetcher=*/nullptr);
mock_provider_->UpdateChromePolicy(policy);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
HeadlessBrowserTestWithPolicy::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(::switches::kRemoteDebuggingPort, "0");
}
void SetUpInProcessBrowserTestFixture() override {
HeadlessBrowserTestWithPolicy::SetUpInProcessBrowserTestFixture();
capture_stderr_.StartCapture();
}
bool expect_remote_debugging_available() { return GetParam(); }
CaptureStdErr capture_stderr_;
};
INSTANTIATE_TEST_SUITE_P(HeadlessBrowserTestWithRemoteDebuggingAllowedPolicy,
HeadlessBrowserTestWithRemoteDebuggingAllowedPolicy,
testing::Values(true, false));
// Remote debugging with ephemeral port is not working on Fuchsia, see
// crbug.com/1209251.
#if BUILDFLAG(IS_FUCHSIA)
#define MAYBE_RemoteDebuggingDisallowed DISABLED_RemoteDebuggingDisallowed
#else
#define MAYBE_RemoteDebuggingDisallowed RemoteDebuggingDisallowed
#endif
IN_PROC_BROWSER_TEST_P(HeadlessBrowserTestWithRemoteDebuggingAllowedPolicy,
MAYBE_RemoteDebuggingDisallowed) {
// DevTools starts its remote debugging port listener asynchronously and
// there is no reliable way to know when it is started, so resort to an
// ugly wait then check captured stderr.
base::PlatformThread::Sleep(TestTimeouts::action_timeout());
capture_stderr_.StopCapture();
std::vector<std::string> captured_lines =
base::SplitString(capture_stderr_.TakeCapturedData(), "\n",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
enum { kUnknown, kDisallowed, kListening } remote_debugging_state = kUnknown;
for (const std::string& line : captured_lines) {
LOG(INFO) << "stderr: " << line;
if (base::MatchPattern(line, "DevTools remote debugging is disallowed *")) {
EXPECT_EQ(remote_debugging_state, kUnknown);
remote_debugging_state = kDisallowed;
} else if (base::MatchPattern(line, "DevTools listening on *")) {
EXPECT_EQ(remote_debugging_state, kUnknown);
remote_debugging_state = kListening;
}
}
EXPECT_NE(remote_debugging_state, kUnknown);
if (expect_remote_debugging_available())
EXPECT_EQ(remote_debugging_state, kListening);
else
EXPECT_EQ(remote_debugging_state, kDisallowed);
}
} // namespace headless