910e62b5创建于 1月15日历史提交
// 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_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "net/base/features.h"

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::AmountOfPhysicalMemory().InMiB() >
           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.
    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.
    return true;
  }
  return false;
}

bool CanCrossSiteNavigationsProactivelySwapBrowsingInstances() {
  return IsBackForwardCacheEnabled();
}

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,
#if BUILDFLAG(IS_ANDROID)
    RenderDocumentLevel::kAllFrames,
#else
    RenderDocumentLevel::kSubframe,
#endif
    &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,
    bool client_overrides_level) {
  if (must_be_replaced) {
    return true;
  }
  if (!has_committed_any_navigation) {
    return false;
  }
  if (client_overrides_level) {
    // If the client overrides the level, allow swapping regardless of the
    // level.
    return true;
  }
  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() {
  return GetNavigationQueueingFeatureLevel() >=
         NavigationQueueingFeatureLevel::kAvoidRedundantCancellations;
}

bool ShouldQueueNavigationsWhenPendingCommitRFHExists() {
  return GetNavigationQueueingFeatureLevel() ==
         NavigationQueueingFeatureLevel::kFull;
}

bool ShouldCreateSiteInstanceForDataUrls() {
  return base::FeatureList::IsEnabled(features::kSiteInstanceGroupsForDataUrls);
}

bool ShouldUseDefaultSiteInstanceGroup() {
  return GetContentClient()->ShouldAllowDefaultSiteInstanceGroup() &&
         base::FeatureList::IsEnabled(features::kDefaultSiteInstanceGroups);
}

}  // namespace content