blob: ec88e4aae7953f21e3c7443cf01d6115c3e20fb0 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/chrome_content_browser_client.h"
#include <memory>
#include <vector>
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/site_isolation/site_isolation_policy.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/search/instant_test_base.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_switches.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/test_native_theme.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#include "extensions/common/extension_urls.h"
#include "url/url_constants.h"
#endif
#if defined(OS_MACOSX)
#include "chrome/test/base/launchservices_utils_mac.h"
#endif
namespace content {
namespace {
enum class NetworkServiceState {
kDisabled,
kEnabled,
};
} // namespace
// Use a test class with SetUpCommandLine to ensure the flag is sent to the
// first renderer process.
class ChromeContentBrowserClientBrowserTest : public InProcessBrowserTest {
public:
ChromeContentBrowserClientBrowserTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
IsolateAllSitesForTesting(command_line);
}
private:
DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClientBrowserTest);
};
// Test that a basic navigation works in --site-per-process mode. This prevents
// regressions when that mode calls out into the ChromeContentBrowserClient,
// such as http://crbug.com/164223.
IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientBrowserTest,
SitePerProcessNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), url);
NavigationEntry* entry = browser()
->tab_strip_model()
->GetWebContentsAt(0)
->GetController()
.GetLastCommittedEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_EQ(url, entry->GetURL());
EXPECT_EQ(url, entry->GetVirtualURL());
}
class ChromeContentBrowserClientPopupsTest : public InProcessBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
// Required setup for kAllowPopupsDuringPageUnload switch
// as its being checked (whether its going to be enabled or not)
// only if the process type is renderer process.
command_line_.AppendSwitchASCII(switches::kProcessType,
switches::kRendererProcess);
}
void SetUpOnMainThread() override {
kChildProcessId = browser()
->tab_strip_model()
->GetActiveWebContents()
->GetMainFrame()
->GetProcess()
->GetID();
}
ChromeContentBrowserClientPopupsTest()
: command_line_(base::CommandLine::NO_PROGRAM) {}
void AppendContentBrowserClientSwitches() {
client_.AppendExtraCommandLineSwitches(&command_line_, kChildProcessId);
}
const base::CommandLine& command_line() const { return command_line_; }
private:
ChromeContentBrowserClient client_;
base::CommandLine command_line_;
int kChildProcessId;
};
IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientPopupsTest,
AllowPopupsDuringPageUnload) {
// Verify that the switch is included only when the
// pref AllowPopupsDuringPageUnload value is true.
PrefService* pref_service = browser()->profile()->GetPrefs();
pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, false);
AppendContentBrowserClientSwitches();
EXPECT_FALSE(
command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
// When the pref value is being set to true
// the switch should be included.
pref_service->SetBoolean(prefs::kAllowPopupsDuringPageUnload, true);
AppendContentBrowserClientSwitches();
EXPECT_TRUE(command_line().HasSwitch(switches::kAllowPopupsDuringPageUnload));
}
// Helper class to mark "https://ntp.com/" as an isolated origin.
class IsolatedOriginNTPBrowserTest : public InProcessBrowserTest,
public InstantTestBase {
public:
IsolatedOriginNTPBrowserTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(https_test_server().InitializeAndListen());
// Mark ntp.com (with an appropriate port from the test server) as an
// isolated origin.
GURL isolated_url(https_test_server().GetURL("ntp.com", "/"));
command_line->AppendSwitchASCII(switches::kIsolateOrigins,
isolated_url.spec());
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_test_server().StartAcceptingConnections();
}
private:
DISALLOW_COPY_AND_ASSIGN(IsolatedOriginNTPBrowserTest);
};
// Verifies that when the remote NTP URL has an origin which is also marked as
// an isolated origin (i.e., requiring a dedicated process), the NTP URL
// still loads successfully, and the resulting process is marked as an Instant
// process. See https://crbug.com/755595.
IN_PROC_BROWSER_TEST_F(IsolatedOriginNTPBrowserTest,
IsolatedOriginDoesNotInterfereWithNTP) {
GURL base_url =
https_test_server().GetURL("ntp.com", "/instant_extended.html");
GURL ntp_url =
https_test_server().GetURL("ntp.com", "/instant_extended_ntp.html");
InstantTestBase::Init(base_url, ntp_url, false);
SetupInstant(browser());
// Sanity check that a SiteInstance for a generic ntp.com URL requires a
// dedicated process.
content::BrowserContext* context = browser()->profile();
GURL isolated_url(https_test_server().GetURL("ntp.com", "/title1.html"));
scoped_refptr<SiteInstance> site_instance =
SiteInstance::CreateForURL(context, isolated_url);
EXPECT_TRUE(site_instance->RequiresDedicatedProcess());
// The site URL for the NTP URL should resolve to a chrome-search:// URL via
// GetEffectiveURL(), even if the NTP URL matches an isolated origin.
GURL site_url(content::SiteInstance::GetSiteForURL(context, ntp_url));
EXPECT_TRUE(site_url.SchemeIs(chrome::kChromeSearchScheme));
// Navigate to the NTP URL and verify that the resulting process is marked as
// an Instant process.
ui_test_utils::NavigateToURL(browser(), ntp_url);
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
InstantService* instant_service =
InstantServiceFactory::GetForProfile(browser()->profile());
EXPECT_TRUE(instant_service->IsInstantProcess(
contents->GetMainFrame()->GetProcess()->GetID()));
// Navigating to a non-NTP URL on ntp.com should not result in an Instant
// process.
ui_test_utils::NavigateToURL(browser(), isolated_url);
EXPECT_FALSE(instant_service->IsInstantProcess(
contents->GetMainFrame()->GetProcess()->GetID()));
}
enum class SitePerProcessMemoryThreshold {
kNone,
k128MB,
k768MB,
};
enum class SitePerProcessMode {
kDisabled,
kEnabled,
kIsolatedOrigin,
};
struct SitePerProcessMemoryThresholdBrowserTestParams {
SitePerProcessMemoryThreshold threshold;
SitePerProcessMode mode;
};
const url::Origin& GetTrialOrigin() {
static base::NoDestructor<url::Origin> origin{
url::Origin::Create(GURL("http://foo.com/"))};
return *origin;
}
// Helper class to run tests on a simulated 512MB low-end device.
class SitePerProcessMemoryThresholdBrowserTest
: public InProcessBrowserTest,
public ::testing::WithParamInterface<
SitePerProcessMemoryThresholdBrowserTestParams> {
public:
SitePerProcessMemoryThresholdBrowserTest() {
switch (GetParam().threshold) {
case SitePerProcessMemoryThreshold::kNone:
break;
case SitePerProcessMemoryThreshold::k128MB:
threshold_feature_.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName,
"128"}});
break;
case SitePerProcessMemoryThreshold::k768MB:
threshold_feature_.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName,
"768"}});
break;
}
switch (GetParam().mode) {
case SitePerProcessMode::kDisabled:
mode_feature_.InitAndDisableFeature(features::kSitePerProcess);
break;
case SitePerProcessMode::kEnabled:
mode_feature_.InitAndEnableFeature(features::kSitePerProcess);
break;
case SitePerProcessMode::kIsolatedOrigin:
mode_feature_.InitAndEnableFeatureWithParameters(
features::kIsolateOrigins,
{{features::kIsolateOriginsFieldTrialParamName,
GetTrialOrigin().Serialize()}});
break;
}
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
// This way the test always sees the same amount of physical memory
// (kLowMemoryDeviceThresholdMB = 512MB), regardless of how much memory is
// available in the testing environment.
command_line->AppendSwitch(switches::kEnableLowEndDeviceMode);
EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB());
}
// Some command-line switches override field trials - the tests need to be
// skipped in this case.
bool ShouldSkipBecauseOfConflictingCommandLineSwitches() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSitePerProcess))
return true;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolation))
return true;
return false;
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
// Initializing the expected embedder origins at runtime is required for
// GetWebstoreLaunchURL(), which needs to have a proper ExtensionsClient
// initialized.
#if !defined(OS_ANDROID)
expected_embedder_origins_.push_back(
url::Origin::Create(GaiaUrls::GetInstance()->gaia_url()));
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
expected_embedder_origins_.push_back(
url::Origin::Create(extension_urls::GetWebstoreLaunchURL()));
#endif
}
protected:
// These are the origins we expect to be returned by
// content::ChildProcessSecurityPolicy::GetIsolatedOrigins() even if
// ContentBrowserClient::ShouldDisableSiteIsolation() returns true.
std::vector<url::Origin> expected_embedder_origins_;
#if defined(OS_ANDROID)
// On Android we don't expect any trial origins because the 512MB
// physical memory used for testing is below the Android specific
// hardcoded 1024MB memory limit that disables site isolation.
const std::size_t kExpectedTrialOrigins = 0;
#else
// All other platforms expect the single trial origin to be returned because
// they don't have the memory limit that disables site isolation.
const std::size_t kExpectedTrialOrigins = 1;
#endif
private:
base::test::ScopedFeatureList threshold_feature_;
base::test::ScopedFeatureList mode_feature_;
DISALLOW_COPY_AND_ASSIGN(SitePerProcessMemoryThresholdBrowserTest);
};
using SitePerProcessMemoryThresholdBrowserTestNoIsolation =
SitePerProcessMemoryThresholdBrowserTest;
IN_PROC_BROWSER_TEST_P(SitePerProcessMemoryThresholdBrowserTestNoIsolation,
NoIsolation) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// Isolation should be disabled given the set of parameters used to
// instantiate these tests.
EXPECT_FALSE(
content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
}
using SitePerProcessMemoryThresholdBrowserTestIsolation =
SitePerProcessMemoryThresholdBrowserTest;
IN_PROC_BROWSER_TEST_P(SitePerProcessMemoryThresholdBrowserTestIsolation,
Isolation) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// Isolation should be enabled given the set of parameters used to
// instantiate these tests.
EXPECT_TRUE(content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
}
INSTANTIATE_TEST_SUITE_P(
NoIsolation,
SitePerProcessMemoryThresholdBrowserTestNoIsolation,
testing::Values(
#if defined(OS_ANDROID)
// Expect no isolation on Android because 512MB physical memory
// triggered by kEnableLowEndDeviceMode in SetUpCommandLine() is below
// the 1024MB Android specific memory limit which disables site
// isolation for all sites.
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::kNone, SitePerProcessMode::kEnabled},
#endif
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k768MB,
SitePerProcessMode::kEnabled},
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::kNone,
SitePerProcessMode::kDisabled},
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k128MB,
SitePerProcessMode::kDisabled},
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k768MB,
SitePerProcessMode::kDisabled}));
INSTANTIATE_TEST_SUITE_P(Isolation,
SitePerProcessMemoryThresholdBrowserTestIsolation,
testing::Values(
#if !defined(OS_ANDROID)
// See the note above regarding why this
// expectation is different on Android.
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::kNone,
SitePerProcessMode::kEnabled},
#endif
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k128MB,
SitePerProcessMode::kEnabled}));
using SitePerProcessMemoryThresholdBrowserTestNoIsolatedOrigin =
SitePerProcessMemoryThresholdBrowserTest;
IN_PROC_BROWSER_TEST_P(SitePerProcessMemoryThresholdBrowserTestNoIsolatedOrigin,
TrialNoIsolatedOrigin) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
EXPECT_EQ(expected_embedder_origins_.size(), isolated_origins.size());
// Verify that the expected embedder origins are present even though site
// isolation has been disabled and the trial origins should not be present.
EXPECT_THAT(expected_embedder_origins_,
::testing::IsSubsetOf(isolated_origins));
// Verify that the trial origin is not present.
EXPECT_THAT(isolated_origins,
::testing::Not(::testing::Contains(GetTrialOrigin())));
}
using SitePerProcessMemoryThresholdBrowserTestIsolatedOrigin =
SitePerProcessMemoryThresholdBrowserTest;
IN_PROC_BROWSER_TEST_P(SitePerProcessMemoryThresholdBrowserTestIsolatedOrigin,
TrialIsolatedOrigin) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
EXPECT_EQ(1u + expected_embedder_origins_.size(), isolated_origins.size());
EXPECT_THAT(expected_embedder_origins_,
::testing::IsSubsetOf(isolated_origins));
// Verify that the trial origin is present.
EXPECT_THAT(isolated_origins, ::testing::Contains(GetTrialOrigin()));
}
INSTANTIATE_TEST_SUITE_P(
TrialNoIsolatedOrigin,
SitePerProcessMemoryThresholdBrowserTestNoIsolatedOrigin,
testing::Values(
#if defined(OS_ANDROID)
// The 512MB the test simulates is below the global Android threshold of
// 1024MB, so the test origin should not be isolated.
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::kNone,
SitePerProcessMode::kIsolatedOrigin},
#endif
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k768MB,
SitePerProcessMode::kIsolatedOrigin}));
INSTANTIATE_TEST_SUITE_P(TrialIsolatedOrigin,
SitePerProcessMemoryThresholdBrowserTestIsolatedOrigin,
testing::Values(
#if defined(OS_ANDROID)
// See the note above regarding why this
// expectation is different on Android.
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::kNone,
SitePerProcessMode::kIsolatedOrigin},
#endif
SitePerProcessMemoryThresholdBrowserTestParams{
SitePerProcessMemoryThreshold::k128MB,
SitePerProcessMode::kIsolatedOrigin}));
// Helper class to run tests with password-triggered site isolation initialized
// via a regular field trial and *not* via a command-line override. It
// creates a new field trial (with 100% probability of being in the group), and
// initializes the test class's ScopedFeatureList using it. Two derived
// classes below control are used to initialize the feature to either enabled
// or disabled state.
class PasswordSiteIsolationFieldTrialTest
: public SitePerProcessMemoryThresholdBrowserTest {
public:
explicit PasswordSiteIsolationFieldTrialTest(bool should_enable)
: field_trial_list_(std::make_unique<base::MockEntropyProvider>()) {
const std::string kTrialName = "PasswordSiteIsolation";
const std::string kGroupName = "FooGroup"; // unused
scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
feature_list->RegisterFieldTrialOverride(
features::kSiteIsolationForPasswordSites.name,
should_enable
? base::FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE
: base::FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE,
trial.get());
feature_list_.InitWithFeatureList(std::move(feature_list));
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// This test creates and tests its own field trial group, so it needs to
// disable the field trial testing config, which might define an
// incompatible trial name/group.
command_line->AppendSwitch(
variations::switches::kDisableFieldTrialTestingConfig);
SitePerProcessMemoryThresholdBrowserTest::SetUpCommandLine(command_line);
}
protected:
base::test::ScopedFeatureList feature_list_;
base::FieldTrialList field_trial_list_;
private:
DISALLOW_COPY_AND_ASSIGN(PasswordSiteIsolationFieldTrialTest);
};
class EnabledPasswordSiteIsolationFieldTrialTest
: public PasswordSiteIsolationFieldTrialTest {
public:
EnabledPasswordSiteIsolationFieldTrialTest()
: PasswordSiteIsolationFieldTrialTest(true /* should_enable */) {}
private:
DISALLOW_COPY_AND_ASSIGN(EnabledPasswordSiteIsolationFieldTrialTest);
};
class DisabledPasswordSiteIsolationFieldTrialTest
: public PasswordSiteIsolationFieldTrialTest {
public:
DisabledPasswordSiteIsolationFieldTrialTest()
: PasswordSiteIsolationFieldTrialTest(false /* should_enable */) {}
private:
DISALLOW_COPY_AND_ASSIGN(DisabledPasswordSiteIsolationFieldTrialTest);
};
IN_PROC_BROWSER_TEST_F(EnabledPasswordSiteIsolationFieldTrialTest,
DISABLED_BelowThreshold) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// If no memory threshold is defined, password site isolation should be
// enabled.
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Define a memory threshold at 768MB. Since this is above the 512MB of
// physical memory that this test simulates, password site isolation should
// now be disabled.
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "768"}});
EXPECT_FALSE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Simulate enabling password site isolation from command line. (Note that
// InitAndEnableFeature uses ScopedFeatureList::InitFromCommandLine
// internally, and that triggering the feature via chrome://flags follows the
// same override path as well.)
base::test::ScopedFeatureList password_site_isolation_feature;
password_site_isolation_feature.InitAndEnableFeature(
features::kSiteIsolationForPasswordSites);
// This should override the memory threshold and enable password site
// isolation.
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
}
IN_PROC_BROWSER_TEST_F(EnabledPasswordSiteIsolationFieldTrialTest,
DISABLED_AboveThreshold) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// If no memory threshold is defined, password site isolation should be
// enabled.
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Define a memory threshold at 128MB. Since this is below the 512MB of
// physical memory that this test simulates, password site isolation should
// still be enabled.
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "128"}});
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Simulate disabling password site isolation from command line. (Note that
// InitAndEnableFeature uses ScopedFeatureList::InitFromCommandLine
// internally, and that triggering the feature via chrome://flags follows the
// same override path as well.) This should take precedence over the regular
// field trial behavior.
base::test::ScopedFeatureList password_site_isolation_feature;
password_site_isolation_feature.InitAndDisableFeature(
features::kSiteIsolationForPasswordSites);
EXPECT_FALSE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
}
// This test verifies that when password-triggered site isolation is disabled
// via field trials but force-enabled via command line, it takes effect even
// when below the memory threshold. See https://crbug.com/1009828.
IN_PROC_BROWSER_TEST_F(DisabledPasswordSiteIsolationFieldTrialTest,
DISABLED_CommandLineOverride_BelowThreshold) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// Password site isolation should be disabled at this point.
EXPECT_FALSE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Simulate enabling password site isolation from command line. (Note that
// InitAndEnableFeature uses ScopedFeatureList::InitFromCommandLine
// internally, and that triggering the feature via chrome://flags follows the
// same override path as well.)
base::test::ScopedFeatureList password_site_isolation_feature;
password_site_isolation_feature.InitAndEnableFeature(
features::kSiteIsolationForPasswordSites);
// If no memory threshold is defined, password site isolation should be
// enabled.
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
// Define a memory threshold at 768MB. This is above the 512MB of physical
// memory that this test simulates, but password site isolation should still
// be enabled, because the test has simulated the user manually overriding
// this feature via command line.
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "768"}});
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
}
// Similar to the test above, but with device memory being above memory
// threshold.
IN_PROC_BROWSER_TEST_F(DisabledPasswordSiteIsolationFieldTrialTest,
DISABLED_CommandLineOverride_AboveThreshold) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
EXPECT_FALSE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
base::test::ScopedFeatureList password_site_isolation_feature;
password_site_isolation_feature.InitAndEnableFeature(
features::kSiteIsolationForPasswordSites);
// If no memory threshold is defined, password site isolation should be
// enabled.
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "128"}});
EXPECT_TRUE(::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled());
}
// Helper class to run tests with strict origin isolation initialized via
// a regular field trial and *not* via a command-line override. It creates a
// new field trial (with 100% probability of being in the group), and
// initializes the test class's ScopedFeatureList using it. Two derived
// classes below control are used to initialize the feature to either enabled
// or disabled state.
class StrictOriginIsolationFieldTrialTest
: public SitePerProcessMemoryThresholdBrowserTest {
public:
explicit StrictOriginIsolationFieldTrialTest(bool should_enable)
: field_trial_list_(std::make_unique<base::MockEntropyProvider>()) {
const std::string kTrialName = "StrictOriginIsolation";
const std::string kGroupName = "FooGroup"; // unused
scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
feature_list->RegisterFieldTrialOverride(
features::kStrictOriginIsolation.name,
should_enable
? base::FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE
: base::FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE,
trial.get());
feature_list_.InitWithFeatureList(std::move(feature_list));
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// This test creates and tests its own field trial group, so it needs to
// disable the field trial testing config, which might define an
// incompatible trial name/group.
command_line->AppendSwitch(
variations::switches::kDisableFieldTrialTestingConfig);
SitePerProcessMemoryThresholdBrowserTest::SetUpCommandLine(command_line);
}
protected:
base::test::ScopedFeatureList feature_list_;
base::FieldTrialList field_trial_list_;
private:
DISALLOW_COPY_AND_ASSIGN(StrictOriginIsolationFieldTrialTest);
};
class EnabledStrictOriginIsolationFieldTrialTest
: public StrictOriginIsolationFieldTrialTest {
public:
EnabledStrictOriginIsolationFieldTrialTest()
: StrictOriginIsolationFieldTrialTest(true /* should_enable */) {}
private:
DISALLOW_COPY_AND_ASSIGN(EnabledStrictOriginIsolationFieldTrialTest);
};
class DisabledStrictOriginIsolationFieldTrialTest
: public StrictOriginIsolationFieldTrialTest {
public:
DisabledStrictOriginIsolationFieldTrialTest()
: StrictOriginIsolationFieldTrialTest(false /* should_enable */) {}
private:
DISALLOW_COPY_AND_ASSIGN(DisabledStrictOriginIsolationFieldTrialTest);
};
// Check that when strict origin isolation is enabled via a field trial, and
// the device is above the memory threshold, disabling it via the command line
// takes precedence.
IN_PROC_BROWSER_TEST_F(EnabledStrictOriginIsolationFieldTrialTest,
DISABLED_DisabledViaCommandLineOverride) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// If no memory threshold is defined, strict origin isolation should be
// enabled.
EXPECT_TRUE(SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
// Define a memory threshold at 128MB. Since this is below the 512MB of
// physical memory that this test simulates, strict origin isolation should
// still be enabled.
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "128"}});
EXPECT_TRUE(SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
// Simulate disabling strict origin isolation from command line. (Note that
// InitAndEnableFeature uses ScopedFeatureList::InitFromCommandLine
// internally, and that disabling the feature via chrome://flags follows the
// same override path as well.)
base::test::ScopedFeatureList strict_origin_isolation_feature;
strict_origin_isolation_feature.InitAndDisableFeature(
features::kStrictOriginIsolation);
EXPECT_FALSE(SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
}
// This test verifies that when strict origin isolation is disabled
// via field trials but force-enabled via command line, it takes effect even
// when below the memory threshold. See https://crbug.com/1009828.
IN_PROC_BROWSER_TEST_F(DisabledStrictOriginIsolationFieldTrialTest,
DISABLED_EnabledViaCommandLineOverride_BelowThreshold) {
if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
return;
// Strict origin isolation should be disabled at this point.
EXPECT_FALSE(content::SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
// Simulate enabling strict origin isolation from command line. (Note that
// InitAndEnableFeature uses ScopedFeatureList::InitFromCommandLine
// internally, and that triggering the feature via chrome://flags follows the
// same override path as well.)
base::test::ScopedFeatureList strict_origin_isolation_feature;
strict_origin_isolation_feature.InitAndEnableFeature(
features::kStrictOriginIsolation);
// If no memory threshold is defined, strict origin isolation should be
// enabled.
EXPECT_TRUE(SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
// Define a memory threshold at 768MB. This is above the 512MB of physical
// memory that this test simulates, but strict origin isolation should still
// be enabled, because the test has simulated the user manually overriding
// this feature via command line.
base::test::ScopedFeatureList memory_feature;
memory_feature.InitAndEnableFeatureWithParameters(
features::kSitePerProcessOnlyForHighMemoryClients,
{{features::kSitePerProcessOnlyForHighMemoryClientsParamName, "768"}});
EXPECT_TRUE(SiteIsolationPolicy::IsStrictOriginIsolationEnabled());
}
// Helper class to test window creation from NTP.
class OpenWindowFromNTPBrowserTest : public InProcessBrowserTest,
public InstantTestBase {
public:
OpenWindowFromNTPBrowserTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(https_test_server().InitializeAndListen());
https_test_server().StartAcceptingConnections();
}
private:
DISALLOW_COPY_AND_ASSIGN(OpenWindowFromNTPBrowserTest);
};
// Test checks that navigations from NTP tab to URLs with same host as NTP but
// different path do not reuse NTP SiteInstance. See https://crbug.com/859062
// for details.
IN_PROC_BROWSER_TEST_F(OpenWindowFromNTPBrowserTest,
TransferFromNTPCreateNewTab) {
GURL search_url =
https_test_server().GetURL("ntp.com", "/instant_extended.html");
GURL ntp_url =
https_test_server().GetURL("ntp.com", "/instant_extended_ntp.html");
InstantTestBase::Init(search_url, ntp_url, false);
SetupInstant(browser());
// Navigate to the NTP URL and verify that the resulting process is marked as
// an Instant process.
ui_test_utils::NavigateToURL(browser(), ntp_url);
content::WebContents* ntp_tab =
browser()->tab_strip_model()->GetActiveWebContents();
InstantService* instant_service =
InstantServiceFactory::GetForProfile(browser()->profile());
EXPECT_TRUE(instant_service->IsInstantProcess(
ntp_tab->GetMainFrame()->GetProcess()->GetID()));
// Execute script that creates new window from ntp tab with
// ntp.com/title1.html as target url. Host is same as remote-ntp host, yet
// path is different.
GURL generic_url(https_test_server().GetURL("ntp.com", "/title1.html"));
content::TestNavigationObserver opened_tab_observer(nullptr);
opened_tab_observer.StartWatchingNewWebContents();
EXPECT_TRUE(
ExecuteScript(ntp_tab, "window.open('" + generic_url.spec() + "');"));
opened_tab_observer.Wait();
ASSERT_EQ(2, browser()->tab_strip_model()->count());
content::WebContents* opened_tab =
browser()->tab_strip_model()->GetActiveWebContents();
// Wait until newly opened tab is fully loaded.
EXPECT_TRUE(WaitForLoadStop(opened_tab));
EXPECT_NE(opened_tab, ntp_tab);
EXPECT_EQ(generic_url, opened_tab->GetLastCommittedURL());
// New created tab should not reside in an Instant process.
EXPECT_FALSE(instant_service->IsInstantProcess(
opened_tab->GetMainFrame()->GetProcess()->GetID()));
}
class ProtocolHandlerTest : public InProcessBrowserTest {
public:
ProtocolHandlerTest() = default;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
protected:
void AddProtocolHandler(const std::string& scheme,
const std::string& redirect_template) {
protocol_handler_registry()->OnAcceptRegisterProtocolHandler(
ProtocolHandler::CreateProtocolHandler(scheme,
GURL(redirect_template)));
}
ProtocolHandlerRegistry* protocol_handler_registry() {
return ProtocolHandlerRegistryFactory::GetInstance()->GetForBrowserContext(
browser()->profile());
}
private:
DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerTest);
};
IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, CustomHandler) {
#if defined(OS_MACOSX)
ASSERT_TRUE(test::RegisterAppWithLaunchServices());
#endif
AddProtocolHandler("news", "https://abc.xyz/?url=%s");
ui_test_utils::NavigateToURL(browser(), GURL("news:something"));
base::string16 expected_title = base::ASCIIToUTF16("abc.xyz");
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
// This is a regression test for crbug.com/969177.
IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, HandlersIgnoredWhenDisabled) {
AddProtocolHandler("bitcoin", "https://abc.xyz/?url=%s");
protocol_handler_registry()->Disable();
ui_test_utils::NavigateToURL(browser(), GURL("bitcoin:something"));
base::string16 tab_title;
ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
EXPECT_EQ(base::ASCIIToUTF16("about:blank"), tab_title);
}
#if defined(OS_CHROMEOS)
// Tests that if a protocol handler is registered for a scheme, an external
// program (another Chrome tab in this case) is not launched to handle the
// navigation. This is a regression test for crbug.com/963133.
IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, ExternalProgramNotLaunched) {
ui_test_utils::NavigateToURL(browser(), GURL("mailto:bob@example.com"));
// If an external program (Chrome) was launched, it will result in a second
// tab being opened.
EXPECT_EQ(1, browser()->tab_strip_model()->count());
// Make sure the protocol handler redirected the navigation.
base::string16 expected_title = base::ASCIIToUTF16("mail.google.com");
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
#endif
} // namespace content