blob: 6ecb54a245530a5a95c9141a15aa0883c7a95d88 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/site_isolation_policy.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/site_isolation_mode.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "third_party/blink/public/common/features.h"
#include "url/origin.h"
namespace content {
namespace {
bool g_disable_flag_caching_for_tests = false;
bool IsDisableSiteIsolationFlagPresent() {
static const bool site_isolation_disabled =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolation);
if (g_disable_flag_caching_for_tests) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolation);
}
return site_isolation_disabled;
}
#if BUILDFLAG(IS_ANDROID)
bool IsDisableSiteIsolationForPolicyFlagPresent() {
static const bool site_isolation_disabled_by_policy =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolationForPolicy);
if (g_disable_flag_caching_for_tests) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSiteIsolationForPolicy);
}
return site_isolation_disabled_by_policy;
}
#endif
bool IsSiteIsolationDisabled(SiteIsolationMode site_isolation_mode) {
if (IsDisableSiteIsolationFlagPresent()) {
return true;
}
#if BUILDFLAG(IS_ANDROID)
// Desktop platforms no longer support disabling Site Isolation by policy.
if (IsDisableSiteIsolationForPolicyFlagPresent()) {
return true;
}
#endif
// Check with the embedder. In particular, chrome/ uses this to disable site
// isolation when below a memory threshold.
return GetContentClient() &&
GetContentClient()->browser()->ShouldDisableSiteIsolation(
site_isolation_mode);
}
url::Origin RemovePort(const url::Origin& origin) {
return url::Origin::CreateFromNormalizedTuple(origin.scheme(), origin.host(),
/*port=*/0);
}
base::flat_set<url::Origin> CreateIsolatedAppOriginSet() {
std::string cmdline_origins(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kIsolatedAppOrigins));
std::vector<std::string> origin_strings = base::SplitString(
cmdline_origins, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
base::flat_set<url::Origin> origin_set;
for (const std::string& origin_string : origin_strings) {
GURL allowed_url(origin_string);
url::Origin allowed_origin = url::Origin::Create(allowed_url);
if (!allowed_origin.opaque()) {
// Site isolation is currently based on Site URLs, which don't include
// ports. Ideally we'd use origin-based isolation for the origins in
// kIsolatedAppOrigins, but long term the origins used in the flag will
// be equivalent to their Site URL-ified version. Because of this, we
// just remove the port here instead of hooking up origin-based isolation
// that won't be needed long term.
if (allowed_url.has_port()) {
LOG(WARNING) << "Ignoring port number for Isolated App origin: "
<< allowed_origin;
}
origin_set.insert(RemovePort(allowed_origin));
} else {
LOG(ERROR) << "Error parsing Isolated App origin: " << origin_string;
}
}
return origin_set;
}
const base::flat_set<url::Origin>& GetIsolatedAppOriginSet() {
static base::NoDestructor<base::flat_set<url::Origin>> kIsolatedAppOrigins(
CreateIsolatedAppOriginSet());
return *kIsolatedAppOrigins;
}
} // namespace
// static
bool SiteIsolationPolicy::UseDedicatedProcessesForAllSites() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSitePerProcess)) {
return true;
}
if (IsSiteIsolationDisabled(SiteIsolationMode::kStrictSiteIsolation))
return false;
// The switches above needs to be checked first, because if the
// ContentBrowserClient consults a base::Feature, then it will activate the
// field trial and assigns the client either to a control or an experiment
// group - such assignment should be final.
return GetContentClient() &&
GetContentClient()->browser()->ShouldEnableStrictSiteIsolation();
}
// static
bool SiteIsolationPolicy::AreIsolatedSandboxedIframesEnabled() {
return !IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation) &&
base::FeatureList::IsEnabled(
blink::features::kIsolateSandboxedIframes);
}
// static
bool SiteIsolationPolicy::AreIsolatedOriginsEnabled() {
// NOTE: Because it is possible for --isolate-origins to be isolating origins
// at a finer-than-site granularity, we do not suppress --isolate-origins when
// --site-per-process is also enabled.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kIsolateOrigins)) {
return true;
}
if (IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation))
return false;
// The feature needs to be checked last, because checking the feature
// activates the field trial and assigns the client either to a control or an
// experiment group - such assignment should be final.
return base::FeatureList::IsEnabled(features::kIsolateOrigins);
}
// static
bool SiteIsolationPolicy::IsStrictOriginIsolationEnabled() {
// If the feature is explicitly enabled by the user (e.g., from
// chrome://flags), honor this regardless of checks to disable site isolation
// below. This means this takes precedence over memory thresholds or
// switches to disable site isolation.
if (base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
features::kStrictOriginIsolation.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
return true;
}
// TODO(wjmaclean): Figure out what should happen when this feature is
// combined with --isolate-origins.
//
// TODO(alexmos): For now, use the same memory threshold for strict origin
// isolation and strict site isolation. In the future, strict origin
// isolation may need its own memory threshold.
if (IsSiteIsolationDisabled(SiteIsolationMode::kStrictSiteIsolation))
return false;
// The feature needs to be checked last, because checking the feature
// activates the field trial and assigns the client either to a control or an
// experiment group - such assignment should be final.
return base::FeatureList::IsEnabled(features::kStrictOriginIsolation);
}
// static
bool SiteIsolationPolicy::IsErrorPageIsolationEnabled(bool in_main_frame) {
return GetContentClient()->browser()->ShouldIsolateErrorPage(in_main_frame);
}
// static
bool SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled() {
return !IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation);
}
// static
bool SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled() {
if (IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation))
return false;
// Currently, preloaded isolated origins are redundant when full site
// isolation is enabled. This may be true on Android if full site isolation
// is enabled manually or via field trials.
if (UseDedicatedProcessesForAllSites())
return false;
return true;
}
// static
bool SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled() {
// If strict site isolation is in use (either by default on desktop or via a
// user opt-in on Android), unconditionally enable opt-in origin isolation.
if (UseDedicatedProcessesForAllSites())
return true;
// Otherwise, if site isolation is disabled (e.g., on Android due to being
// under a memory threshold), turn off opt-in origin isolation.
if (IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation))
return false;
return IsOriginAgentClusterEnabled();
}
// static
bool SiteIsolationPolicy::IsOriginAgentClusterEnabled() {
return base::FeatureList::IsEnabled(features::kOriginIsolationHeader);
}
// static
bool SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled() {
// If the user has explicitly enabled site isolation for COOP sites from the
// command line, honor this regardless of policies that may disable site
// isolation.
if (base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
features::kSiteIsolationForCrossOriginOpenerPolicy.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
return true;
}
// Don't apply COOP isolation if site isolation has been disabled (e.g., due
// to memory thresholds).
if (!SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled())
return false;
// COOP isolation is only needed on platforms where strict site isolation is
// not used.
if (UseDedicatedProcessesForAllSites())
return false;
// The feature needs to be checked last, because checking the feature
// activates the field trial and assigns the client either to a control or an
// experiment group - such assignment should be final.
return base::FeatureList::IsEnabled(
features::kSiteIsolationForCrossOriginOpenerPolicy);
}
// static
bool SiteIsolationPolicy::ShouldPersistIsolatedCOOPSites() {
if (!IsSiteIsolationForCOOPEnabled())
return false;
return features::kSiteIsolationForCrossOriginOpenerPolicyShouldPersistParam
.Get();
}
// static
bool SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled() {
return base::FeatureList::IsEnabled(features::kSiteIsolationForGuests);
}
// static
std::string SiteIsolationPolicy::GetIsolatedOriginsFromCommandLine() {
std::string cmdline_arg =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kIsolateOrigins);
return cmdline_arg;
}
std::string SiteIsolationPolicy::GetIsolatedOriginsFromFieldTrial() {
std::string origins;
// Check if site isolation modes are turned off (e.g., due to an opt-out
// flag).
if (IsSiteIsolationDisabled(SiteIsolationMode::kPartialSiteIsolation))
return origins;
// The feature needs to be checked after the opt-out, because checking the
// feature activates the field trial and assigns the client either to a
// control or an experiment group - such assignment should be final.
if (base::FeatureList::IsEnabled(features::kIsolateOrigins)) {
origins = base::GetFieldTrialParamValueByFeature(
features::kIsolateOrigins,
features::kIsolateOriginsFieldTrialParamName);
}
return origins;
}
void SiteIsolationPolicy::ApplyGlobalIsolatedOrigins() {
ChildProcessSecurityPolicy* policy =
ChildProcessSecurityPolicy::GetInstance();
std::string from_cmdline = GetIsolatedOriginsFromCommandLine();
policy->AddFutureIsolatedOrigins(
from_cmdline,
ChildProcessSecurityPolicy::IsolatedOriginSource::COMMAND_LINE);
std::string from_trial = GetIsolatedOriginsFromFieldTrial();
policy->AddFutureIsolatedOrigins(
from_trial,
ChildProcessSecurityPolicy::IsolatedOriginSource::FIELD_TRIAL);
std::vector<url::Origin> from_embedder =
GetContentClient()->browser()->GetOriginsRequiringDedicatedProcess();
policy->AddFutureIsolatedOrigins(
from_embedder,
ChildProcessSecurityPolicy::IsolatedOriginSource::BUILT_IN);
}
// static
bool SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel(
BrowserContext* browser_context,
const GURL& url) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
url::Origin origin = RemovePort(url::Origin::Create(url));
bool origin_matches_flag = g_disable_flag_caching_for_tests
? CreateIsolatedAppOriginSet().contains(origin)
: GetIsolatedAppOriginSet().contains(origin);
return GetContentClient()->browser()->ShouldUrlUseApplicationIsolationLevel(
browser_context, url, origin_matches_flag);
}
// static
void SiteIsolationPolicy::DisableFlagCachingForTesting() {
g_disable_flag_caching_for_tests = true;
}
// static
bool SiteIsolationPolicy::IsProcessIsolationForFencedFramesEnabled() {
// If the user has explicitly enabled process isolation for fenced frames from
// the command line, honor this regardless of policies that may disable site
// isolation.
if (base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
features::kIsolateFencedFrames.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
return true;
}
return UseDedicatedProcessesForAllSites() &&
base::FeatureList::IsEnabled(features::kIsolateFencedFrames);
}
} // namespace content