blob: 42dc136b53ea04b013276ac98aabc55d05b84db0 [file] [log] [blame]
// Copyright 2020 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/common/content_navigation_policy.h"
#include <bitset>
#include "base/command_line.h"
#include "base/metrics/field_trial_params.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "content/common/features.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "net/base/features.h"
namespace features {
BASE_FEATURE(kBackForwardCache_NoMemoryLimit_Trial,
"BackForwardCache_NoMemoryLimit_Trial",
base::FEATURE_ENABLED_BY_DEFAULT);
}
namespace content {
bool DeviceHasEnoughMemoryForBackForwardCache() {
// This method make sure that the physical memory of device is greater than
// the allowed threshold and enables back-forward cache if the feature
// kBackForwardCacheMemoryControls is enabled.
// It is important to check the base::FeatureList to avoid activating any
// field trial groups if BFCache is disabled due to memory threshold.
if (base::FeatureList::IsEnabled(features::kBackForwardCacheMemoryControls)) {
// On Android, BackForwardCache is enabled for devices with 1200MB memory or
// above.
int default_memory_threshold_mb =
#if BUILDFLAG(IS_ANDROID)
1200;
#else
// Desktop has lower memory limitations compared to Android allowing us
// to enable BackForwardCache for all devices.
0;
#endif
int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
features::kBackForwardCacheMemoryControls,
"memory_threshold_for_back_forward_cache_in_mb",
default_memory_threshold_mb);
return base::SysInfo::AmountOfPhysicalMemoryMB() > memory_threshold_mb;
}
// If the feature kBackForwardCacheMemoryControls is not enabled, all the
// devices are included by default.
return true;
}
bool IsBackForwardCacheDisabledByCommandLine() {
if (base::CommandLine::InitializedForCurrentProcess() &&
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableBackForwardCache)) {
return true;
}
return false;
}
bool IsBackForwardCacheEnabled() {
bool has_enough_memory = DeviceHasEnoughMemoryForBackForwardCache();
if (!has_enough_memory) {
// When the device does not have enough memory for BackForwardCache, return
// false so we won't try to put things in the back/forward cache.
// Also, trigger the activation of the BackForwardCache_NoMemoryLimit_Trial
// field trial by querying the feature flag. With this, we guarantee that
// all devices that do not have enough memory for BackForwardCache will be
// included in that field trial. See case #1 in the comment for the
// BackForwardCache_NoMemoryLimit_Trial in the header file for more details.
base::FeatureList::IsEnabled(
features::kBackForwardCache_NoMemoryLimit_Trial);
return false;
}
if (IsBackForwardCacheDisabledByCommandLine())
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. This allows us to keep
// the BackForwardCache field trial to include only devices that have enough
// memory for BackForwardCache, and those devices only.
if (base::FeatureList::IsEnabled(features::kBackForwardCache)) {
// When the device does have enough memory for BackForwardCache, return
// true so we won't try to put things in the back/forward cache. Also,
// trigger the activation of the BackForwardCache_NoMemoryLimit_Trial field
// trial by querying the feature flag. With this, we guarantee that all
// devices that do have enough memory for BackForwardCache and have the
// BackForwardCache feature flag enabled will be included in that field
// trial. See case #2 in the comment for the
// BackForwardCache_NoMemoryLimit_Trial in the header file for more details.
base::FeatureList::IsEnabled(
features::kBackForwardCache_NoMemoryLimit_Trial);
return true;
}
return false;
}
bool CanCrossSiteNavigationsProactivelySwapBrowsingInstances() {
return IsBackForwardCacheEnabled();
}
const char kRenderDocumentLevelParameterName[] = "level";
constexpr base::FeatureParam<RenderDocumentLevel>::Option
render_document_levels[] = {
{RenderDocumentLevel::kCrashedFrame, "crashed-frame"},
{RenderDocumentLevel::kNonLocalRootSubframe, "non-local-root-subframe"},
{RenderDocumentLevel::kSubframe, "subframe"},
{RenderDocumentLevel::kAllFrames, "all-frames"}};
const base::FeatureParam<RenderDocumentLevel> render_document_level{
&features::kRenderDocument, kRenderDocumentLevelParameterName,
RenderDocumentLevel::kCrashedFrame, &render_document_levels};
RenderDocumentLevel GetRenderDocumentLevel() {
if (base::FeatureList::IsEnabled(features::kRenderDocument))
return render_document_level.Get();
return RenderDocumentLevel::kCrashedFrame;
}
std::string GetRenderDocumentLevelName(RenderDocumentLevel level) {
return render_document_level.GetName(level);
}
bool ShouldCreateNewRenderFrameHostOnSameSiteNavigation(
bool is_main_frame,
bool is_local_root,
bool has_committed_any_navigation,
bool must_be_replaced) {
if (must_be_replaced) {
return true;
}
if (!has_committed_any_navigation) {
return false;
}
RenderDocumentLevel level = GetRenderDocumentLevel();
if (is_main_frame) {
CHECK(is_local_root);
return level >= RenderDocumentLevel::kAllFrames;
}
if (is_local_root) {
return level >= RenderDocumentLevel::kSubframe;
}
return level >= RenderDocumentLevel::kNonLocalRootSubframe;
}
bool ShouldCreateNewHostForAllFrames() {
return GetRenderDocumentLevel() >= RenderDocumentLevel::kAllFrames;
}
bool ShouldSkipEarlyCommitPendingForCrashedFrame() {
static bool skip_early_commit_pending_for_crashed_frame =
base::FeatureList::IsEnabled(
features::kSkipEarlyCommitPendingForCrashedFrame);
return skip_early_commit_pending_for_crashed_frame;
}
static constexpr base::FeatureParam<NavigationQueueingFeatureLevel>::Option
kNavigationQueueingFeatureLevels[] = {
{NavigationQueueingFeatureLevel::kNone, "none"},
{NavigationQueueingFeatureLevel::kAvoidRedundantCancellations,
"avoid-redundant"},
{NavigationQueueingFeatureLevel::kFull, "full"}};
const base::FeatureParam<NavigationQueueingFeatureLevel>
kNavigationQueueingFeatureLevelParam{
&features::kQueueNavigationsWhileWaitingForCommit, "queueing_level",
NavigationQueueingFeatureLevel::kFull,
&kNavigationQueueingFeatureLevels};
NavigationQueueingFeatureLevel GetNavigationQueueingFeatureLevel() {
if (GetRenderDocumentLevel() >= RenderDocumentLevel::kNonLocalRootSubframe) {
// When RenderDocument is enabled with a level of "non-local-root-subframe"
// or more, navigation queueing needs to be enabled too, to avoid crashes.
return NavigationQueueingFeatureLevel::kFull;
}
if (base::FeatureList::IsEnabled(
features::kQueueNavigationsWhileWaitingForCommit)) {
return kNavigationQueueingFeatureLevelParam.Get();
}
return NavigationQueueingFeatureLevel::kNone;
}
bool ShouldAvoidRedundantNavigationCancellations() {
// If the experimental early RenderFrameHost swap for history navigations is
// turned on, this must return true so that when the old RFH is unloaded as
// part of the early swap, this doesn't cancel the navigation that's still
// ongoing in the new RFH.
if (base::FeatureList::IsEnabled(
features::kEarlyDocumentSwapForBackForwardTransitions)) {
return true;
}
return GetNavigationQueueingFeatureLevel() >=
NavigationQueueingFeatureLevel::kAvoidRedundantCancellations;
}
bool ShouldQueueNavigationsWhenPendingCommitRFHExists() {
return GetNavigationQueueingFeatureLevel() ==
NavigationQueueingFeatureLevel::kFull;
}
bool ShouldRestrictCanAccessDataForOriginToUIThread() {
// Only restrict calls to the UI thread if the feature is enabled, and if the
// new blob URL support is enabled.
return base::FeatureList::IsEnabled(
features::kRestrictCanAccessDataForOriginToUIThread) &&
base::FeatureList::IsEnabled(
net::features::kSupportPartitionedBlobUrl);
}
bool ShouldCreateSiteInstanceForDataUrls() {
return base::FeatureList::IsEnabled(features::kSiteInstanceGroupsForDataUrls);
}
} // namespace content