#include "content/browser/renderer_host/back_forward_cache_impl.h"
#include <algorithm>
#include <list>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "arkweb/build/features/features.h"
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/enum_set.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "content/browser/bad_message.h"
#include "content/browser/renderer_host/back_forward_cache_can_store_document_result.h"
#include "content/browser/renderer_host/back_forward_cache_metrics.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/visible_time_request_trigger.h"
#include "content/browser/webid/idp_network_request_manager.h"
#include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/visibility.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
#include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom.h"
#include "third_party/blink/public/mojom/frame/sudden_termination_disabler_type.mojom-shared.h"
#include "third_party/blink/public/mojom/script_source_location.mojom.h"
#if BUILDFLAG(IS_ANDROID)
#include "content/public/browser/android/child_process_importance.h"
#endif
#if BUILDFLAG(ARKWEB_BFCACHE)
#include "arkweb/chromium_ext/content/browser/renderer_host/ark_web_back_forward_cache_impl.h"
#include "arkweb/chromium_ext/base/ohos/sys_info_utils_ext.h"
#endif
namespace content {
class RenderProcessHostInternalObserver;
BASE_FEATURE(kBackForwardCacheSize, base::FEATURE_ENABLED_BY_DEFAULT);
const base::FeatureParam<int> kBackForwardCacheSizeCacheSize{
&kBackForwardCacheSize, "cache_size", 6};
const base::FeatureParam<int> kBackForwardCacheSizeForegroundCacheSize{
&kBackForwardCacheSize, "foreground_cache_size", 0};
namespace {
using blink::scheduler::WebSchedulerTrackedFeature;
using blink::scheduler::WebSchedulerTrackedFeatures;
static constexpr int kDefaultTimeToLiveInBackForwardCacheInSeconds = 600;
#if BUILDFLAG(IS_ANDROID)
bool IsProcessBindingEnabled() {
if (!IsBackForwardCacheEnabled()) {
return false;
}
const std::string process_binding_param =
base::GetFieldTrialParamValueByFeature(features::kBackForwardCache,
"process_binding_strength");
return process_binding_param.empty() || process_binding_param == "DISABLE";
}
const base::FeatureParam<ChildProcessImportance>::Option
child_process_importance_options[] = {
{ChildProcessImportance::IMPORTANT, "IMPORTANT"},
{ChildProcessImportance::MODERATE, "MODERATE"},
{ChildProcessImportance::PERCEPTIBLE, "PERCEPTIBLE"},
{ChildProcessImportance::NORMAL, "NORMAL"}};
const base::FeatureParam<ChildProcessImportance> kChildProcessImportanceParam{
&features::kBackForwardCache, "process_binding_strength",
ChildProcessImportance::MODERATE, &child_process_importance_options};
#endif
WebSchedulerTrackedFeatures SupportedFeaturesImpl() {
WebSchedulerTrackedFeatures features;
#if BUILDFLAG(ARKWEB_BFCACHE)
if (GetArkWebBackForwardCacheFeatures(features)) {
return features;
}
#endif
if (!IsBackForwardCacheEnabled()) {
return features;
}
static constexpr base::FeatureParam<std::string> supported_features(
&features::kBackForwardCache, "supported_features", "");
std::vector<std::string> tokens =
base::SplitString(supported_features.Get(), ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const std::string& token : tokens) {
auto feature = blink::scheduler::StringToFeature(token);
if (feature.has_value()) {
features.Put(feature.value());
} else if (!blink::scheduler::IsRemovedFeature(token)) {
DLOG(WARNING) << "Invalid feature string: " << token;
}
}
return features;
}
WebSchedulerTrackedFeatures SupportedFeatures() {
static WebSchedulerTrackedFeatures features = SupportedFeaturesImpl();
return features;
}
bool IgnoresOutstandingNetworkRequestForTesting() {
if (!IsBackForwardCacheEnabled()) {
return false;
}
static constexpr base::FeatureParam<bool>
outstanding_network_request_supported(
&features::kBackForwardCache,
"ignore_outstanding_network_request_for_testing", false);
return outstanding_network_request_supported.Get();
}
bool ShouldIgnoreBlocklists() {
if (!IsBackForwardCacheEnabled()) {
return false;
}
static constexpr base::FeatureParam<bool> should_ignore_blocklists(
&features::kBackForwardCache, "should_ignore_blocklists", false);
return should_ignore_blocklists.Get();
}
WebSchedulerTrackedFeatures GetDisallowedWebSchedulerTrackedFeatures() {
return {WebSchedulerTrackedFeature::kBroadcastChannel,
WebSchedulerTrackedFeature::kContainsPlugins,
WebSchedulerTrackedFeature::kDummy,
WebSchedulerTrackedFeature::kIdleManager,
WebSchedulerTrackedFeature::kIndexedDBEvent,
WebSchedulerTrackedFeature::kKeyboardLock,
WebSchedulerTrackedFeature::kKeepaliveRequest,
WebSchedulerTrackedFeature::kLiveMediaStreamTrack,
WebSchedulerTrackedFeature::kPaymentManager,
WebSchedulerTrackedFeature::kPictureInPicture,
WebSchedulerTrackedFeature::kPrinting,
WebSchedulerTrackedFeature::kRequestedAudioCapturePermission,
WebSchedulerTrackedFeature::kRequestedBackForwardCacheBlockedSensors,
WebSchedulerTrackedFeature::kRequestedBackgroundWorkPermission,
WebSchedulerTrackedFeature::kRequestedMIDIPermission,
WebSchedulerTrackedFeature::kRequestedVideoCapturePermission,
WebSchedulerTrackedFeature::kSmartCard,
WebSchedulerTrackedFeature::kSharedWorker,
WebSchedulerTrackedFeature::kSpeechRecognizer,
WebSchedulerTrackedFeature::kUnloadHandler,
WebSchedulerTrackedFeature::kWebAuthentication,
WebSchedulerTrackedFeature::kWebBluetooth,
WebSchedulerTrackedFeature::kWebHID,
WebSchedulerTrackedFeature::kWebLocks,
WebSchedulerTrackedFeature::kWebOTPService,
WebSchedulerTrackedFeature::kWebRTC,
WebSchedulerTrackedFeature::kWebShare,
WebSchedulerTrackedFeature::kWebSocket,
WebSchedulerTrackedFeature::kWebTransport,
WebSchedulerTrackedFeature::kWebXR,
WebSchedulerTrackedFeature::kParserAborted,
#if BUILDFLAG(ARKWEB_BFCACHE)
WebSchedulerTrackedFeature::kEnableCacheNativeEmbed,
WebSchedulerTrackedFeature::kEnableCacheMediaTakeOver,
#endif
WebSchedulerTrackedFeature::kSharedWorkerMessage};
}
WebSchedulerTrackedFeatures GetInjectionWebSchedulerTrackedFeatures() {
return {WebSchedulerTrackedFeature::kInjectedJavascript,
WebSchedulerTrackedFeature::kInjectedStyleSheet};
}
WebSchedulerTrackedFeatures GetNetworkWebSchedulerTrackedFeatures() {
return {WebSchedulerTrackedFeature::kOutstandingNetworkRequestOthers,
WebSchedulerTrackedFeature::kOutstandingNetworkRequestFetch,
WebSchedulerTrackedFeature::kOutstandingNetworkRequestXHR};
}
WebSchedulerTrackedFeatures GetDisallowedForCacheControlNoStoreFeatures() {
return {WebSchedulerTrackedFeature::kWebSocketSticky,
WebSchedulerTrackedFeature::kWebRTCSticky,
WebSchedulerTrackedFeature::kWebTransportSticky,
WebSchedulerTrackedFeature::
kJsNetworkRequestReceivedCacheControlNoStoreResource};
}
WebSchedulerTrackedFeatures GetAllowedWebSchedulerTrackedFeatures() {
return {
WebSchedulerTrackedFeature::kDocumentLoaded,
WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoCache,
WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoStore,
WebSchedulerTrackedFeature::kOutstandingNetworkRequestDirectSocket,
WebSchedulerTrackedFeature::kRequestedStorageAccessGrant,
WebSchedulerTrackedFeature::kSubresourceHasCacheControlNoCache,
WebSchedulerTrackedFeature::kSubresourceHasCacheControlNoStore,
WebSchedulerTrackedFeature::kWebNfc,
};
}
WebSchedulerTrackedFeatures
GetNonBackForwardCacheAffectingWebSchedulerTrackedFeatures() {
return {WebSchedulerTrackedFeature::kWebSerial};
}
std::string GetAllowedURLList() {
if (!IsBackForwardCacheEnabled()) {
if (base::FeatureList::IsEnabled(
kRecordBackForwardCacheMetricsWithoutEnabling)) {
return base::GetFieldTrialParamValueByFeature(
kRecordBackForwardCacheMetricsWithoutEnabling, "allowed_websites");
}
return "";
}
return base::GetFieldTrialParamValueByFeature(features::kBackForwardCache,
"allowed_websites");
}
std::string GetBlockedURLList() {
return IsBackForwardCacheEnabled()
? base::GetFieldTrialParamValueByFeature(
features::kBackForwardCache, "blocked_websites")
: "";
}
std::string GetBlockedCgiParams() {
return IsBackForwardCacheEnabled()
? base::GetFieldTrialParamValueByFeature(
features::kBackForwardCache, "blocked_cgi_params")
: "";
}
base::flat_map<std::string, std::vector<std::string>> ParseCommaSeparatedURLs(
std::string_view comma_separated_urls) {
base::flat_map<std::string, std::vector<std::string>> urls;
for (auto& it :
base::SplitString(comma_separated_urls, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL)) {
GURL url = GURL(it);
urls[url.GetHost()].push_back(url.GetPath());
}
return urls;
}
base::flat_set<std::string> ParseBlockedCgiParams(
std::string_view cgi_params_string) {
return base::SplitString(cgi_params_string, "|", base::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
}
BackForwardCacheTestDelegate* g_bfcache_disabled_test_observer = nullptr;
void RestoreBrowserControlsState(RenderFrameHostImpl* cached_rfh) {
#if BUILDFLAG(ARKWEB_EXT_TOPCONTROLS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableNwebExTopControls)) {
return;
}
#endif
auto* current_rfh =
cached_rfh->frame_tree_node()->render_manager()->current_frame_host();
DCHECK_NE(current_rfh, cached_rfh);
float prev_top_controls_shown_ratio = current_rfh->GetRenderWidgetHost()
->render_frame_metadata_provider()
->LastRenderFrameMetadata()
.top_controls_shown_ratio;
if (prev_top_controls_shown_ratio < 1) {
cached_rfh->GetPage().UpdateBrowserControlsState(
cc::BrowserControlsState::kBoth, cc::BrowserControlsState::kHidden,
false, std::nullopt);
}
}
void RequestRecordTimeToVisible(RenderFrameHostImpl* rfh,
base::TimeTicks navigation_start) {
if (rfh->delegate()->GetVisibility() != Visibility::HIDDEN) {
rfh->GetRenderWidgetHost()->GetVisibleTimeRequestTrigger().UpdateRequest(
navigation_start, false,
false,
true);
}
}
bool HasForegroundedProcess(BackForwardCacheImpl::Entry& entry) {
for (const auto& rvh : entry.render_view_hosts()) {
if (rvh->GetProcess()->GetPriority() !=
base::Process::Priority::kBestEffort) {
return true;
}
}
return false;
}
bool AllRenderViewHostsReceivedAckFromRenderer(
BackForwardCacheImpl::Entry& entry) {
for (const auto& rvh : entry.render_view_hosts()) {
if (!rvh->DidReceiveBackForwardCacheAck()) {
return false;
}
}
return true;
}
enum class CacheControlNoStoreExperimentLevel {
kDoNotStore = 1,
kStoreAndEvictUponRestore = 2,
kStoreAndRestoreUnlessCookieChange = 3,
kStoreAndRestoreUnlessHTTPOnlyCookieChange = 4,
};
const char kCacheControlNoStoreExperimentLevelName[] = "level";
static constexpr base::FeatureParam<CacheControlNoStoreExperimentLevel>::Option
cache_control_levels[] = {
{CacheControlNoStoreExperimentLevel::kStoreAndEvictUponRestore,
"store-and-evict"},
{CacheControlNoStoreExperimentLevel::kStoreAndRestoreUnlessCookieChange,
"restore-unless-cookie-change"},
{CacheControlNoStoreExperimentLevel::
kStoreAndRestoreUnlessHTTPOnlyCookieChange,
"restore-unless-http-only-cookie-change"},
};
const base::FeatureParam<CacheControlNoStoreExperimentLevel>
cache_control_level{
&features::kCacheControlNoStoreEnterBackForwardCache,
kCacheControlNoStoreExperimentLevelName,
CacheControlNoStoreExperimentLevel::kStoreAndRestoreUnlessCookieChange,
&cache_control_levels};
CacheControlNoStoreExperimentLevel GetCacheControlNoStoreLevel() {
if (!IsBackForwardCacheEnabled() ||
!base::FeatureList::IsEnabled(
features::kCacheControlNoStoreEnterBackForwardCache)) {
return CacheControlNoStoreExperimentLevel::kDoNotStore;
}
return cache_control_level.Get();
}
const char kCacheControlNoStoreTimeToLiveName[] = "ttl";
const base::FeatureParam<base::TimeDelta> cache_control_no_store_ttl{
&features::kCacheControlNoStoreEnterBackForwardCache,
kCacheControlNoStoreTimeToLiveName, base::Minutes(3)};
base::TimeDelta GetCacheControlNoStoreTTL() {
return cache_control_no_store_ttl.Get();
}
enum class BackForwardCachePrioritizedEntryExperimentLevel {
kDoNotPrioritize = 0,
kPrioritizeUnlessShouldClearAll = 1,
kPrioritizeUnlessShouldClearAllAndNoEviction = 2,
};
const char kBackForwardCachePrioritizedEntryExperimentLevelName[] = "level";
static constexpr base::FeatureParam<
BackForwardCachePrioritizedEntryExperimentLevel>::Option
prioritized_entry_levels[] = {
{BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize,
"do-not-prioritize"},
{BackForwardCachePrioritizedEntryExperimentLevel::
kPrioritizeUnlessShouldClearAll,
"prioritize-unless-should-clear-all"},
{BackForwardCachePrioritizedEntryExperimentLevel::
kPrioritizeUnlessShouldClearAllAndNoEviction,
"prioritize-unless-should-clear-all-and-no-eviction"},
};
const base::FeatureParam<BackForwardCachePrioritizedEntryExperimentLevel>
prioritized_entry_level{
&kBackForwardCachePrioritizedEntry,
kBackForwardCachePrioritizedEntryExperimentLevelName,
BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize,
&prioritized_entry_levels};
BackForwardCachePrioritizedEntryExperimentLevel
GetBackForwardCachePrioritizedEntryExperimentLevel() {
if (!IsBackForwardCacheEnabled() ||
!base::FeatureList::IsEnabled(kBackForwardCachePrioritizedEntry)) {
return BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize;
}
return prioritized_entry_level.Get();
}
bool IsSameOriginForTreeResult(RenderFrameHostImpl* rfh,
const url::Origin& main_document_origin) {
if (rfh->IsNestedWithinFencedFrame()) {
return false;
}
return rfh->GetLastCommittedOrigin().IsSameOriginWith(main_document_origin);
}
void MarkNoWithSingleFeature(BackForwardCacheCanStoreDocumentResult* result,
WebSchedulerTrackedFeature feature) {
BackForwardCacheCanStoreDocumentResult::BlockingDetailsMap map;
auto details_ptr = blink::mojom::BlockingDetails::New();
details_ptr->feature = feature;
map[feature].push_back(std::move(details_ptr));
result->NoDueToFeatures(std::move(map));
}
void MarkNoWithMultipleFeatures(BackForwardCacheCanStoreDocumentResult* result,
RenderFrameHostImpl* rfh,
WebSchedulerTrackedFeatures features) {
BackForwardCacheCanStoreDocumentResult::BlockingDetailsMap map;
WebSchedulerTrackedFeatures features_added;
for (const auto& details : rfh->GetBackForwardCacheBlockingDetails()) {
auto feature = static_cast<blink::scheduler::WebSchedulerTrackedFeature>(
details->feature);
if (!features.Has(feature)) {
continue;
}
map[feature].push_back(details.Clone());
features_added.Put(feature);
}
result->NoDueToFeatures(std::move(map));
DCHECK(features == features_added);
}
}
BlockListedFeatures BackForwardCacheImpl::GetAllowedFeatures(
RequestedFeatures requested_features,
CacheControlNoStoreContext ccns_context) {
WebSchedulerTrackedFeatures result =
Union(GetAllowedWebSchedulerTrackedFeatures(),
GetNonBackForwardCacheAffectingWebSchedulerTrackedFeatures());
result.PutAll(GetInjectionWebSchedulerTrackedFeatures());
if (IgnoresOutstandingNetworkRequestForTesting()) {
result.PutAll(GetNetworkWebSchedulerTrackedFeatures());
}
result.PutAll(SupportedFeatures());
if (requested_features == RequestedFeatures::kOnlySticky) {
WebSchedulerTrackedFeatures non_sticky =
Difference(GetDisallowedWebSchedulerTrackedFeatures(),
blink::scheduler::StickyFeatures());
if (!IgnoresOutstandingNetworkRequestForTesting()) {
non_sticky.PutAll(Difference(GetNetworkWebSchedulerTrackedFeatures(),
blink::scheduler::StickyFeatures()));
}
result.PutAll(non_sticky);
}
if (IsUnloadAllowed()) {
result.Put(WebSchedulerTrackedFeature::kUnloadHandler);
}
if (ccns_context == kNotInCCNSContext) {
result.PutAll(GetDisallowedForCacheControlNoStoreFeatures());
}
return result;
}
BlockListedFeatures BackForwardCacheImpl::GetDisallowedFeatures(
RequestedFeatures requested_features,
CacheControlNoStoreContext ccns_context) {
WebSchedulerTrackedFeatures result =
GetDisallowedWebSchedulerTrackedFeatures();
if (!IgnoresOutstandingNetworkRequestForTesting()) {
result.PutAll(GetNetworkWebSchedulerTrackedFeatures());
}
result.RemoveAll(SupportedFeatures());
if (requested_features == RequestedFeatures::kOnlySticky) {
result = Intersection(result, blink::scheduler::StickyFeatures());
}
if (IsUnloadAllowed()) {
result.Remove(WebSchedulerTrackedFeature::kUnloadHandler);
}
if (ccns_context == kInCCNSContext) {
result.PutAll(GetDisallowedForCacheControlNoStoreFeatures());
}
return result;
}
BackForwardCacheImpl::MessageHandlingPolicyWhenCached
BackForwardCacheImpl::GetChannelAssociatedMessageHandlingPolicy() {
if (!IsBackForwardCacheEnabled()) {
return kMessagePolicyNone;
}
static constexpr char kFieldTrialParam[] = "message_handling_when_cached";
auto param = base::GetFieldTrialParamValueByFeature(
features::kBackForwardCache, kFieldTrialParam);
if (param.empty() || param == "log") {
return kMessagePolicyLog;
} else if (param == "none") {
return kMessagePolicyNone;
} else if (param == "dump") {
return kMessagePolicyDump;
} else {
DLOG(WARNING) << "Failed to parse field trial param " << kFieldTrialParam
<< " with string value " << param
<< " under feature kBackForwardCache"
<< features::kBackForwardCache.name;
return kMessagePolicyLog;
}
}
BackForwardCacheImpl::Entry::Entry(std::unique_ptr<StoredPage> stored_page)
: stored_page_(std::move(stored_page)) {}
BackForwardCacheImpl::Entry::~Entry() = default;
void BackForwardCacheImpl::Entry::WriteIntoTrace(
perfetto::TracedValue context) {
auto dict = std::move(context).WriteDictionary();
dict.Add("render_frame_host", render_frame_host());
}
void BackForwardCacheImpl::RenderProcessPriorityChanged(
RenderProcessHostImpl* host) {
EnforceCacheSizeLimit();
#if BUILDFLAG(ARKWEB_BFCACHE)
LOG(DEBUG) << "[BFCACHE]" << " Now stored entries number is: " << GetStoredEntriesNumber();
#endif
}
BackForwardCacheTestDelegate::BackForwardCacheTestDelegate() {
DCHECK(!g_bfcache_disabled_test_observer);
g_bfcache_disabled_test_observer = this;
}
BackForwardCacheTestDelegate::~BackForwardCacheTestDelegate() {
DCHECK_EQ(g_bfcache_disabled_test_observer, this);
g_bfcache_disabled_test_observer = nullptr;
}
BackForwardCacheImpl::BackForwardCacheImpl(BrowserContext* browser_context)
: allowed_urls_(ParseCommaSeparatedURLs(GetAllowedURLList())),
blocked_urls_(ParseCommaSeparatedURLs(GetBlockedURLList())),
blocked_cgi_params_(ParseBlockedCgiParams(GetBlockedCgiParams())),
weak_factory_(this) {
should_allow_storing_pages_with_cache_control_no_store_ =
browser_context &&
GetContentClient()
->browser()
->ShouldAllowBackForwardCacheForCacheControlNoStorePage(
browser_context) &&
GetCacheControlNoStoreLevel() >
CacheControlNoStoreExperimentLevel::kDoNotStore;
#if BUILDFLAG(ARKWEB_BFCACHE)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableBFCache)) {
this->size_ = 1;
}
#endif
}
BackForwardCacheImpl::~BackForwardCacheImpl() {
Shutdown();
}
std::optional<int> GetFieldTrialParamByFeatureAsOptionalInt(
const base::Feature& feature,
const std::string& param_name) {
std::string value_as_string =
GetFieldTrialParamValueByFeature(feature, param_name);
int value_as_int = 0;
if (base::StringToInt(value_as_string, &value_as_int)) {
return std::optional<int>(value_as_int);
}
return std::optional<int>();
}
base::TimeDelta BackForwardCacheImpl::GetTimeToLiveInBackForwardCache(
CacheControlNoStoreContext ccns_context) {
if (embedder_supplied_time_to_live_.has_value()) {
return embedder_supplied_time_to_live_.value();
}
if (base::FeatureList::IsEnabled(
features::kBackForwardCacheTimeToLiveControl)) {
std::optional<int> time_to_live = GetFieldTrialParamByFeatureAsOptionalInt(
features::kBackForwardCacheTimeToLiveControl, "time_to_live_seconds");
if (time_to_live.has_value()) {
return base::Seconds(time_to_live.value());
}
}
if (base::FeatureList::IsEnabled(kBackForwardCacheNoTimeEviction)) {
return base::TimeDelta::Max();
}
if (ccns_context == kInCCNSContext) {
return GetCacheControlNoStoreTTL();
} else {
return base::Seconds(kDefaultTimeToLiveInBackForwardCacheInSeconds);
}
}
size_t BackForwardCacheImpl::GetCacheSize() {
if (!IsBackForwardCacheEnabled()) {
return 0;
}
if (embedder_supplied_cache_size_.has_value()) {
return embedder_supplied_cache_size_.value();
}
if (base::FeatureList::IsEnabled(kBackForwardCacheSize)) {
return kBackForwardCacheSizeCacheSize.Get();
}
return 0;
}
size_t BackForwardCacheImpl::GetForegroundedEntriesCacheSize() {
if (!IsBackForwardCacheEnabled()) {
return 0;
}
if (embedder_supplied_cache_size_.has_value()) {
return 0;
}
if (base::FeatureList::IsEnabled(kBackForwardCacheSize)) {
return kBackForwardCacheSizeForegroundCacheSize.Get();
}
return 0;
}
bool BackForwardCacheImpl::UsingForegroundBackgroundCacheSizeLimit() {
return GetForegroundedEntriesCacheSize() > 0;
}
BackForwardCacheImpl::Entry* BackForwardCacheImpl::FindMatchingEntry(
PageImpl& page) {
RenderFrameHostImpl* render_frame_host = &page.GetMainDocument();
Entry* matching_entry = nullptr;
for (std::unique_ptr<Entry>& entry : entries_) {
if (render_frame_host == entry->render_frame_host()) {
matching_entry = entry.get();
break;
}
}
return matching_entry;
}
void BackForwardCacheImpl::UpdateCanStoreToIncludeCacheControlNoStore(
BackForwardCacheCanStoreDocumentResult& result,
RenderFrameHostImpl* render_frame_host) {
if (!should_allow_storing_pages_with_cache_control_no_store()) {
return;
}
if (!render_frame_host->LoadedWithCacheControlNoStoreHeader()) {
return;
}
if (render_frame_host->IsDeviceBoundSessionTerminated() &&
base::FeatureList::IsEnabled(
features::kDeviceBoundSessionTerminationEvictBackForwardCache)) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kCacheControlNoStoreDeviceBoundSessionTerminated);
return;
}
const RenderFrameHostImpl::CookieChangeListener::CookieChangeInfo&
cookie_change_info = render_frame_host->GetCookieChangeInfo();
const std::string kCookieCHangeInfoMetricName =
"BackForwardCache.CCNS.CookieChangeInfo.";
base::UmaHistogramCounts1000(
base::StrCat({kCookieCHangeInfoMetricName, "AllCookies"}),
cookie_change_info.cookie_modification_count);
base::UmaHistogramCounts1000(
base::StrCat(
{kCookieCHangeInfoMetricName, "AllCookiesFromMainFrameNavigation"}),
cookie_change_info.cookie_modification_removing_count);
base::UmaHistogramCounts1000(
base::StrCat({kCookieCHangeInfoMetricName, "HttpOnlyCookies"}),
cookie_change_info.http_only_cookie_modification_count);
base::UmaHistogramCounts1000(
base::StrCat({kCookieCHangeInfoMetricName,
"HttpOnlyCookiesFromMainFrameNavigation"}),
cookie_change_info.http_only_cookie_modification_removing_count);
if (cookie_change_info.http_only_cookie_modification_count >
cookie_change_info.http_only_cookie_modification_removing_count) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kCacheControlNoStoreHTTPOnlyCookieModified);
} else if (cookie_change_info.cookie_modification_count >
cookie_change_info.cookie_modification_removing_count) {
if (GetCacheControlNoStoreLevel() <=
CacheControlNoStoreExperimentLevel::
kStoreAndRestoreUnlessCookieChange) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kCacheControlNoStoreCookieModified);
}
} else if (GetCacheControlNoStoreLevel() ==
CacheControlNoStoreExperimentLevel::kStoreAndEvictUponRestore) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore);
}
}
namespace {
void LogAndTraceResult(
const RenderFrameHostImpl& rfh,
const BackForwardCacheCanStoreDocumentResult& flattened_result,
const perfetto::StaticString& caller) {
VLOG(1) << caller.value << ": " << rfh.GetLastCommittedURL() << " : "
<< flattened_result.ToString();
TRACE_EVENT("navigation", caller,
ChromeTrackEvent::kBackForwardCacheCanStoreDocumentResult,
flattened_result);
}
}
BackForwardCacheCanStoreDocumentResultWithTree
BackForwardCacheImpl::GetCurrentBackForwardCacheEligibility(
RenderFrameHostImpl* rfh) {
BackForwardCacheCanStoreDocumentResult flattened;
auto result =
PopulateReasonsForPage(rfh, flattened, RequestedFeatures::kAllIfAcked);
LogAndTraceResult(
*rfh, result.flattened_reasons,
"BackForwardCacheImpl::GetCurrentBackForwardCacheEligibility");
return result;
}
BackForwardCacheCanStoreDocumentResultWithTree
BackForwardCacheImpl::GetCompleteBackForwardCacheEligibilityForReporting(
RenderFrameHostImpl* rfh) {
BackForwardCacheCanStoreDocumentResult flattened;
auto result = PopulateReasonsForPage(rfh, flattened, RequestedFeatures::kAll);
LogAndTraceResult(*rfh, result.flattened_reasons,
"BackForwardCacheImpl::"
"GetCompleteBackForwardCacheEligibilityForReporting");
return result;
}
BackForwardCacheCanStoreDocumentResultWithTree
BackForwardCacheImpl::GetFutureBackForwardCacheEligibilityPotential(
RenderFrameHostImpl* rfh) {
BackForwardCacheCanStoreDocumentResult flattened;
auto result =
PopulateReasonsForPage(rfh, flattened, RequestedFeatures::kOnlySticky);
LogAndTraceResult(
*rfh, result.flattened_reasons,
"BackForwardCacheImpl::GetFutureBackForwardCacheEligibilityPotential");
return result;
}
BackForwardCacheCanStoreDocumentResultWithTree
BackForwardCacheImpl::PopulateReasonsForPage(
RenderFrameHostImpl* rfh,
BackForwardCacheCanStoreDocumentResult& flattened_result,
RequestedFeatures requested_features) {
BackForwardCacheCanStoreDocumentResult main_document_specific_result;
bool main_frame_in_bfcache =
rfh->IsInBackForwardCache() && !rfh->GetParentOrOuterDocumentOrEmbedder();
std::unique_ptr<BackForwardCacheCanStoreTreeResult> result_tree;
if (!rfh->IsInPrimaryMainFrame() && !main_frame_in_bfcache) {
main_document_specific_result.No(
BackForwardCacheMetrics::NotRestoredReason::kNotPrimaryMainFrame);
result_tree = BackForwardCacheCanStoreTreeResult::CreateEmptyTree(rfh);
} else {
PopulateReasonsForMainDocument(main_document_specific_result, rfh);
NotRestoredReasonBuilder builder(rfh, requested_features);
result_tree = builder.GetTreeResult();
flattened_result.AddReasonsFrom(builder.GetFlattenedResult());
}
flattened_result.AddReasonsFrom(main_document_specific_result);
result_tree->AddReasonsToSubtreeRootFrom(main_document_specific_result);
DCHECK_EQ(flattened_result, result_tree->FlattenTree());
return BackForwardCacheCanStoreDocumentResultWithTree(flattened_result,
std::move(result_tree));
}
void BackForwardCacheImpl::PopulateReasonsForMainDocument(
BackForwardCacheCanStoreDocumentResult& result,
RenderFrameHostImpl* rfh) {
bool main_frame_in_bfcache =
rfh->IsInBackForwardCache() && !rfh->GetParentOrOuterDocumentOrEmbedder();
DCHECK(rfh->IsInPrimaryMainFrame() || main_frame_in_bfcache);
if (!rfh->delegate()->IsBackForwardCacheSupported()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kBackForwardCacheDisabledForDelegate);
}
if (!IsBackForwardCacheEnabled() || is_disabled_for_testing_) {
result.No(
BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled);
if (IsBackForwardCacheDisabledByCommandLine()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kBackForwardCacheDisabledByCommandLine);
}
if (!DeviceHasEnoughMemoryForBackForwardCache()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kBackForwardCacheDisabledByLowMemory);
}
}
bool is_active_rfh = rfh->IsActive();
unsigned expected_related_active_contents_count = is_active_rfh ? 1 : 0;
if (!is_active_rfh) {
auto* current_rfh =
rfh->frame_tree_node()->render_manager()->current_frame_host();
if (current_rfh->GetSiteInstance()->IsRelatedSiteInstance(
rfh->GetSiteInstance())) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kBrowsingInstanceNotSwapped);
expected_related_active_contents_count++;
}
}
DCHECK_GE(rfh->GetSiteInstance()->GetRelatedActiveContentsCount(),
expected_related_active_contents_count);
if (rfh->GetSiteInstance()->GetRelatedActiveContentsCount() >
expected_related_active_contents_count) {
std::optional<ShouldSwapBrowsingInstance> browsing_instance_swap_result;
if (auto* metrics = rfh->GetBackForwardCacheMetrics()) {
browsing_instance_swap_result = metrics->browsing_instance_swap_result();
}
result.NoDueToRelatedActiveContents(browsing_instance_swap_result);
}
if (rfh->last_http_status_code() != net::HTTP_OK) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK);
}
if (rfh->IsErrorDocument()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kErrorDocument);
}
if (rfh->last_http_method() != net::HttpRequestHeaders::kGetMethod) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kHTTPMethodNotGET);
}
if (!rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
result.No(
BackForwardCacheMetrics::NotRestoredReason::kSchemeNotHTTPOrHTTPS);
}
if (rfh->LoadedWithCacheControlNoStoreHeader()) {
if (!should_allow_storing_pages_with_cache_control_no_store()) {
MarkNoWithSingleFeature(
&result,
WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoStore);
} else {
if (!rfh->IsFullCookieAccessAllowed()) {
result.No(
BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore);
result.No(BackForwardCacheMetrics::NotRestoredReason::kCookieDisabled);
}
}
}
if (!IsAllowed(rfh->GetLastCommittedURL())) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kDomainNotAllowed);
}
}
void BackForwardCacheImpl::NotRestoredReasonBuilder::
PopulateStickyReasonsForDocument(
BackForwardCacheCanStoreDocumentResult& result,
RenderFrameHostImpl* rfh) {
if (rfh->IsBackForwardCacheDisabled() && !ShouldIgnoreBlocklists()) {
result.NoDueToDisableForRenderFrameHostCalled(
rfh->back_forward_cache_disabled_reasons());
}
if ((rfh->frame_tree()->delegate()->GetOuterDelegateFrameTreeNodeId() &&
rfh->frame_tree()->is_primary()) ||
rfh->frame_tree()->is_guest()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kHaveInnerContents);
}
if (!IsUnloadAllowed()) {
const bool has_unload_handler = rfh->has_unload_handler();
if (has_unload_handler) {
result.No(rfh->GetParent() ? BackForwardCacheMetrics::NotRestoredReason::
kUnloadHandlerExistsInSubFrame
: BackForwardCacheMetrics::NotRestoredReason::
kUnloadHandlerExistsInMainFrame);
}
}
CacheControlNoStoreContext ccns_context = kNotInCCNSContext;
if (root_rfh_->LoadedWithCacheControlNoStoreHeader() &&
rfh->GetLastCommittedOrigin().IsSameOriginWith(
root_rfh_->GetLastCommittedOrigin())) {
ccns_context = kInCCNSContext;
}
WebSchedulerTrackedFeatures banned_features = Intersection(
GetDisallowedFeatures(RequestedFeatures::kOnlySticky, ccns_context),
rfh->GetBackForwardCacheDisablingFeatures());
if (!Intersection(banned_features,
GetDisallowedForCacheControlNoStoreFeatures())
.empty()) {
banned_features.Put(
WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoStore);
rfh->RecordBackForwardCacheDisablingReason(
WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoStore);
}
if (!banned_features.empty()) {
if (!ShouldIgnoreBlocklists()) {
MarkNoWithMultipleFeatures(&result, rfh, banned_features);
}
}
}
void BackForwardCacheImpl::NotRestoredReasonBuilder::
PopulateNonStickyReasonsForDocument(
BackForwardCacheCanStoreDocumentResult& result,
RenderFrameHostImpl* rfh,
RequestedFeatures requested_features) {
DCHECK_NE(requested_features, RequestedFeatures::kOnlySticky);
if (!rfh->IsDOMContentLoaded() && rfh->has_committed_any_navigation()) {
result.No(BackForwardCacheMetrics::NotRestoredReason::kLoading);
}
WebSchedulerTrackedFeatures banned_features = Intersection(
GetDisallowedFeatures(RequestedFeatures::kAll, kNotInCCNSContext),
rfh->GetBackForwardCacheDisablingFeatures());
if (!banned_features.empty() && !ShouldIgnoreBlocklists()) {
if (requested_features == RequestedFeatures::kAll ||
(requested_features == RequestedFeatures::kAllIfAcked &&
rfh->render_view_host()->DidReceiveBackForwardCacheAck())) {
MarkNoWithMultipleFeatures(&result, rfh, banned_features);
}
}
if (rfh->GetParentOrOuterDocument()) {
NavigationRequest* nav_request =
rfh->frame_tree_node()->navigation_request();
if ((nav_request && nav_request->NeedsUrlLoader() &&
(nav_request->HasLoader() ||
nav_request->state() >
NavigationRequest::NavigationState::WILL_START_REQUEST)) ||
rfh->frame_tree_node()->HasPendingCommitNavigation()) {
result.No(
BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating);
}
}
if (base::FeatureList::IsEnabled(blink::features::kBFCacheWithSharedWorker)) {
if (!rfh->IsInBackForwardCache()) {
SharedWorkerServiceImpl* service = static_cast<SharedWorkerServiceImpl*>(
rfh->GetStoragePartition()->GetSharedWorkerService());
if (service && service->EvictBFCachedClientsIfLastActive(rfh)) {
result.No(BackForwardCacheMetrics::NotRestoredReason::
kSharedWorkerWithNoActiveClient);
}
}
}
}
void BackForwardCacheImpl::NotRestoredReasonBuilder::PopulateReasonsForDocument(
BackForwardCacheCanStoreDocumentResult& result,
RenderFrameHostImpl* rfh,
RequestedFeatures requested_features) {
PopulateStickyReasonsForDocument(result, rfh);
if (requested_features != RequestedFeatures::kOnlySticky) {
PopulateNonStickyReasonsForDocument(result, rfh, requested_features);
}
}
BackForwardCacheCanStoreDocumentResultWithTree
BackForwardCacheImpl::CreateEvictionBackForwardCacheCanStoreTreeResult(
RenderFrameHostImpl& rfh,
BackForwardCacheCanStoreDocumentResult& eviction_reason) {
BackForwardCacheImpl::NotRestoredReasonBuilder builder(
rfh.GetOutermostMainFrame(),
RequestedFeatures::kOnlySticky,
BackForwardCacheImpl::NotRestoredReasonBuilder::EvictionInfo(
rfh, &eviction_reason));
BackForwardCacheCanStoreDocumentResult flattened_result =
builder.GetFlattenedResult();
LogAndTraceResult(
rfh, flattened_result,
"BackForwardCacheImpl::CreateEvictionBackForwardCacheCanStoreTreeResult");
return BackForwardCacheCanStoreDocumentResultWithTree(
flattened_result, builder.GetTreeResult());
}
BackForwardCacheImpl::NotRestoredReasonBuilder::NotRestoredReasonBuilder(
RenderFrameHostImpl* root_rfh,
RequestedFeatures requested_features)
: NotRestoredReasonBuilder(root_rfh,
requested_features,
std::nullopt) {}
BackForwardCacheImpl::NotRestoredReasonBuilder::NotRestoredReasonBuilder(
RenderFrameHostImpl* root_rfh,
RequestedFeatures requested_features,
std::optional<EvictionInfo> eviction_info)
: root_rfh_(root_rfh),
bfcache_(root_rfh_->frame_tree_node()
->navigator()
.controller()
.GetBackForwardCache()),
requested_features_(requested_features),
eviction_info_(eviction_info) {
DCHECK(root_rfh_->IsInPrimaryMainFrame() ||
(root_rfh_->IsInBackForwardCache() &&
!root_rfh_->GetParentOrOuterDocumentOrEmbedder()));
std::map<RenderFrameHostImpl*, BackForwardCacheCanStoreTreeResult*>
parent_map;
root_rfh_->ForEachRenderFrameHostImpl([&](RenderFrameHostImpl* rfh) {
auto rfh_result = PopulateReasons(rfh);
parent_map[rfh] = rfh_result.get();
if (rfh == root_rfh_) {
tree_result_ = std::move(rfh_result);
} else {
RenderFrameHostImpl* parent = rfh->GetParentOrOuterDocumentOrEmbedder();
DCHECK(parent);
parent_map[parent]->AppendChild(std::move(rfh_result));
}
});
}
BackForwardCacheImpl::NotRestoredReasonBuilder::~NotRestoredReasonBuilder() =
default;
std::unique_ptr<BackForwardCacheCanStoreTreeResult>
BackForwardCacheImpl::NotRestoredReasonBuilder::PopulateReasons(
RenderFrameHostImpl* rfh) {
BackForwardCacheCanStoreDocumentResult result_for_rfh;
if (eviction_info_.has_value()) {
if (rfh == eviction_info_->rfh_to_be_evicted) {
result_for_rfh.AddReasonsFrom(*(eviction_info_->reasons));
}
} else {
PopulateReasonsForDocument(result_for_rfh, rfh, requested_features_);
}
bfcache_->UpdateCanStoreToIncludeCacheControlNoStore(result_for_rfh, rfh);
flattened_result_.AddReasonsFrom(result_for_rfh);
std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree(
new BackForwardCacheCanStoreTreeResult(
rfh, root_rfh_->GetLastCommittedOrigin(), rfh->GetLastCommittedURL(),
result_for_rfh));
return tree;
}
void BackForwardCacheImpl::StoreEntry(
std::unique_ptr<BackForwardCacheImpl::Entry> entry) {
TRACE_EVENT("navigation", "BackForwardCache::StoreEntry", "entry", entry);
DCHECK(GetCurrentBackForwardCacheEligibility(entry->render_frame_host())
.CanStore());
#if BUILDFLAG(IS_ANDROID)
if (!IsProcessBindingEnabled()) {
RenderWidgetHostImpl* rwh =
entry->render_frame_host()->GetRenderWidgetHost();
ChildProcessImportance current_importance = rwh->importance();
rwh->SetImportance(
std::min(current_importance, kChildProcessImportanceParam.Get()));
}
#endif
entry->render_frame_host()->DidEnterBackForwardCache();
entry->SetStoredPageDelegate(this);
entries_.push_front(std::move(entry));
AddProcessesForEntry(*entries_.front());
EnforceCacheSizeLimit();
#if BUILDFLAG(ARKWEB_BFCACHE)
LOG(DEBUG) << "[BFCACHE] " << __func__ << " Now stored entries number is: " << GetStoredEntriesNumber();
#endif
}
void BackForwardCacheImpl::EnforceCacheSizeLimit() {
if (!IsBackForwardCacheEnabled()) {
return;
}
if (UsingForegroundBackgroundCacheSizeLimit()) {
EnforceCacheSizeLimitInternal(
GetForegroundedEntriesCacheSize(),
BackForwardCacheMetrics::NotRestoredReason::kForegroundCacheLimit);
}
#if BUILDFLAG(ARKWEB_BFCACHE)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableBFCache)) {
EnforceCacheSizeLimitInternal(this->size_,
BackForwardCacheMetrics::NotRestoredReason::kCacheLimitPrunedOnModerateMemoryPressure);
return;
}
#endif
EnforceCacheSizeLimitInternal(
GetCacheSize(), BackForwardCacheMetrics::NotRestoredReason::kCacheLimit);
}
size_t BackForwardCacheImpl::Prune(size_t limit, NotRestoredReason reason) {
return EnforceCacheSizeLimitInternal(limit, reason);
}
size_t BackForwardCacheImpl::EnforceCacheSizeLimitInternal(
size_t limit,
BackForwardCacheMetrics::NotRestoredReason reason) {
using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
using Level = BackForwardCachePrioritizedEntryExperimentLevel;
size_t count = 0;
bool did_evict_any_entry = false;
auto prioritized_level = GetBackForwardCachePrioritizedEntryExperimentLevel();
bool should_skip_eviction_for_prioritized_entry =
(prioritized_level == Level::kPrioritizeUnlessShouldClearAll &&
limit > 0) ||
prioritized_level == Level::kPrioritizeUnlessShouldClearAllAndNoEviction;
for (auto stored_entry_iter = entries_.begin();
stored_entry_iter != entries_.end(); stored_entry_iter++) {
Entry* stored_entry = stored_entry_iter->get();
RenderFrameHostImpl* rfh = stored_entry->render_frame_host();
if (rfh->is_evicted_from_back_forward_cache()) {
continue;
}
if (reason == NotRestoredReason::kForegroundCacheLimit &&
!HasForegroundedProcess(*stored_entry)) {
continue;
}
if (reason !=
NotRestoredReason::kCacheLimitPrunedOnModerateMemoryPressure &&
reason !=
NotRestoredReason::kCacheLimitPrunedOnCriticalMemoryPressure &&
!AllRenderViewHostsReceivedAckFromRenderer(*stored_entry)) {
continue;
}
if (++count > limit) {
if (should_skip_eviction_for_prioritized_entry) {
if (GetContentClient()->browser()->ShouldPrioritizeForBackForwardCache(
rfh->GetBrowserContext(), rfh->GetLastCommittedURL())) {
if (prioritized_entry_ == entries_.end() ||
prioritized_entry_ == stored_entry_iter) {
prioritized_entry_ = stored_entry_iter;
continue;
}
}
}
did_evict_any_entry = true;
rfh->EvictFromBackForwardCacheWithReason(reason);
}
}
if (
prioritized_entry_ != entries_.end() &&
limit == 0 &&
prioritized_level ==
Level::kPrioritizeUnlessShouldClearAllAndNoEviction &&
!did_evict_any_entry) {
prioritized_entry_->get()
->render_frame_host()
->EvictFromBackForwardCacheWithReason(reason);
prioritized_entry_ = entries_.end();
}
return count;
}
void BackForwardCacheImpl::SetEmbedderSuppliedCacheSize(
size_t embedder_supplied_cache_size) {
if (embedder_supplied_cache_size == GetCacheSize()) {
return;
}
embedder_supplied_cache_size_ = embedder_supplied_cache_size;
EnforceCacheSizeLimit();
}
void BackForwardCacheImpl::SetEmbedderSuppliedTimeToLive(
base::TimeDelta embedder_supplied_time_to_live) {
if (embedder_supplied_time_to_live ==
GetTimeToLiveInBackForwardCache(
CacheControlNoStoreContext::kNotInCCNSContext)) {
return;
}
embedder_supplied_time_to_live_ = embedder_supplied_time_to_live;
Flush();
}
std::unique_ptr<BackForwardCacheImpl::Entry> BackForwardCacheImpl::RestoreEntry(
int navigation_entry_id,
blink::mojom::PageRestoreParamsPtr page_restore_params) {
TRACE_EVENT0("navigation", "BackForwardCache::RestoreEntry");
auto matching_entry = std::ranges::find(
entries_, navigation_entry_id, [](std::unique_ptr<Entry>& entry) {
return entry->render_frame_host()->nav_entry_id();
});
if (matching_entry == entries_.end()) {
return nullptr;
}
if ((*matching_entry)
->render_frame_host()
->is_evicted_from_back_forward_cache()) {
return nullptr;
}
std::unique_ptr<Entry> entry = std::move(*matching_entry);
TRACE_EVENT_INSTANT("navigation",
"BackForwardCache::RestoreEntry_matched_entry", "entry",
entry);
entry->SetStoredPageDelegate(nullptr);
if (prioritized_entry_ == matching_entry) {
prioritized_entry_ = entries_.end();
}
entries_.erase(matching_entry);
RemoveProcessesForEntry(*entry);
base::TimeTicks start_time = page_restore_params->navigation_start;
entry->SetPageRestoreParams(std::move(page_restore_params));
RequestRecordTimeToVisible(entry->render_frame_host(), start_time);
entry->render_frame_host()->WillLeaveBackForwardCache();
RestoreBrowserControlsState(entry->render_frame_host());
#if BUILDFLAG(ARKWEB_BFCACHE)
LOG(DEBUG) << "[BFCACHE] " << __func__ << " Now stored entries number is: " << GetStoredEntriesNumber();
#endif
return entry;
}
void BackForwardCacheImpl::Flush() {
Flush(NotRestoredReason::kCacheFlushed);
}
void BackForwardCacheImpl::Flush(NotRestoredReason reason) {
TRACE_EVENT0("navigation", "BackForwardCache::Flush");
for (std::unique_ptr<Entry>& entry : entries_) {
entry->render_frame_host()->EvictFromBackForwardCacheWithReason(reason);
}
}
void BackForwardCacheImpl::Flush(
const StoragePartition::StorageKeyMatcherFunction& storage_key_filter) {
for (std::unique_ptr<Entry>& entry : entries_) {
if (storage_key_filter.Run(blink::StorageKey::CreateFirstParty(
entry->render_frame_host()->GetLastCommittedOrigin()))) {
entry->render_frame_host()->EvictFromBackForwardCacheWithReason(
BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed);
}
}
}
void BackForwardCacheImpl::FlushCacheControlNoStoreEntries(
const StoragePartition::StorageKeyMatcherFunction& storage_key_filter) {
for (std::unique_ptr<Entry>& entry : entries_) {
RenderFrameHostImpl* rfh = entry->render_frame_host();
if (rfh->LoadedWithCacheControlNoStoreHeader() &&
storage_key_filter.Run(blink::StorageKey::CreateFirstParty(
rfh->GetLastCommittedOrigin()))) {
BackForwardCacheCanStoreDocumentResult flattened_reasons;
flattened_reasons.No(
BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore);
flattened_reasons.No(
BackForwardCacheMetrics::NotRestoredReason::kCookieFlushed);
rfh->EvictFromBackForwardCacheWithFlattenedReasons(flattened_reasons);
}
}
}
void BackForwardCacheImpl::Shutdown() {
if (UsingForegroundBackgroundCacheSizeLimit()) {
for (auto& entry : entries_) {
RemoveProcessesForEntry(*entry.get());
}
}
entries_.clear();
}
void BackForwardCacheImpl::EvictFramesInRelatedSiteInstances(
SiteInstance* site_instance) {
for (std::unique_ptr<Entry>& entry : entries_) {
if (entry->render_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
site_instance)) {
entry->render_frame_host()->EvictFromBackForwardCacheWithReason(
BackForwardCacheMetrics::NotRestoredReason::
kConflictingBrowsingInstance);
}
}
}
void BackForwardCacheImpl::PostTaskToDestroyEvictedFrames() {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&BackForwardCacheImpl::DestroyEvictedFrames,
weak_factory_.GetWeakPtr()));
}
bool BackForwardCache::IsBackForwardCacheFeatureEnabled() {
return IsBackForwardCacheEnabled();
}
void BackForwardCache::DisableForRenderFrameHost(
RenderFrameHost* render_frame_host,
DisabledReason reason,
std::optional<ukm::SourceId> source_id) {
DisableForRenderFrameHost(render_frame_host->GetGlobalId(), reason,
source_id);
}
void BackForwardCache::DisableForRenderFrameHost(
GlobalRenderFrameHostId id,
DisabledReason reason,
std::optional<ukm::SourceId> source_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (g_bfcache_disabled_test_observer) {
g_bfcache_disabled_test_observer->OnDisabledForFrameWithReason(id, reason);
}
if (auto* rfh = RenderFrameHostImpl::FromID(id)) {
rfh->DisableBackForwardCache(reason, source_id);
}
}
void BackForwardCache::SetHadFormDataAssociated(Page& page) {
BackForwardCacheMetrics* metrics =
static_cast<RenderFrameHostImpl*>(&page.GetMainDocument())
->GetBackForwardCacheMetrics();
if (metrics) {
metrics->SetHadFormDataAssociated(true);
}
}
void BackForwardCacheImpl::DisableForTesting(DisableForTestingReason reason) {
is_disabled_for_testing_ = true;
Flush();
}
const std::list<std::unique_ptr<BackForwardCacheImpl::Entry>>&
BackForwardCacheImpl::GetEntries() {
return entries_;
}
std::list<BackForwardCacheImpl::Entry*>
BackForwardCacheImpl::GetEntriesForRenderViewHostImpl(
const RenderViewHostImpl* rvhi) const {
std::list<BackForwardCacheImpl::Entry*> entries_for_rvhi;
for (auto& entry : entries_) {
for (const auto& rvh : entry->render_view_hosts()) {
if (&*rvh == rvhi) {
entries_for_rvhi.push_back(entry.get());
break;
}
}
}
return entries_for_rvhi;
}
base::expected<BackForwardCacheImpl::Entry*,
BackForwardCacheImpl::GetEntryFailureCase>
BackForwardCacheImpl::GetOrEvictEntry(int navigation_entry_id) {
auto matching_entry = std::ranges::find(
entries_, navigation_entry_id, [](std::unique_ptr<Entry>& entry) {
return entry->render_frame_host()->nav_entry_id();
});
if (matching_entry == entries_.end()) {
return base::unexpected(
BackForwardCacheImpl::GetEntryFailureCase::kEntryNotFound);
}
auto* render_frame_host = (*matching_entry)->render_frame_host();
if (render_frame_host->is_evicted_from_back_forward_cache()) {
return base::unexpected(
BackForwardCacheImpl::GetEntryFailureCase::kEntryEvictedBefore);
}
BackForwardCacheCanStoreDocumentResultWithTree bfcache_eligibility =
GetCurrentBackForwardCacheEligibility(render_frame_host);
if (!bfcache_eligibility.CanRestore()) {
render_frame_host->EvictFromBackForwardCacheWithFlattenedAndTreeReasons(
bfcache_eligibility);
return base::unexpected(
BackForwardCacheImpl::GetEntryFailureCase::kEntryIneligibleAndEvicted);
}
return (*matching_entry).get();
}
bool BackForwardCacheImpl::HasPotentiallyMatchingEntry(
const RenderFrameHostImpl& commiting_rfh,
const std::optional<url::Origin>& initiator_origin,
bool require_no_subframes) const {
if (commiting_rfh.GetSiteInstance()->GetRelatedActiveContentsCount() > 1) {
return false;
}
for (auto& entry : entries_) {
auto* bfcached_rfh = entry->render_frame_host();
if (require_no_subframes && bfcached_rfh->child_count() > 0) {
continue;
}
if (commiting_rfh.GetLastCommittedURL() ==
bfcached_rfh->GetLastCommittedURL() &&
commiting_rfh.GetLastCommittedOrigin() ==
bfcached_rfh->GetLastCommittedOrigin() &&
bfcached_rfh->last_committed_frame_entry()->initiator_origin() ==
initiator_origin &&
commiting_rfh.policy_container_host()->policies() ==
bfcached_rfh->policy_container_host()->policies()) {
return true;
}
}
return false;
}
void BackForwardCacheImpl::RenderViewHostNoLongerStored(
RenderViewHostImpl* rvh) {
if (!UsingForegroundBackgroundCacheSizeLimit()) {
return;
}
RenderViewHostNoLongerStoredInternal(rvh);
}
void BackForwardCacheImpl::RenderViewHostNoLongerStoredInternal(
RenderViewHostImpl* rvh) {
RenderProcessHostImpl* process =
static_cast<RenderProcessHostImpl*>(rvh->GetProcess());
observed_processes_.erase(observed_processes_.find(process));
if (observed_processes_.find(process) == observed_processes_.end()) {
process->RemoveInternalObserver(this);
}
}
void BackForwardCacheImpl::AddProcessesForEntry(Entry& entry) {
if (!UsingForegroundBackgroundCacheSizeLimit()) {
return;
}
for (const auto& rvh : entry.render_view_hosts()) {
RenderProcessHostImpl* process =
static_cast<RenderProcessHostImpl*>(rvh->GetProcess());
if (observed_processes_.find(process) == observed_processes_.end()) {
process->AddInternalObserver(this);
}
observed_processes_.insert(process);
}
}
void BackForwardCacheImpl::RemoveProcessesForEntry(Entry& entry) {
if (!UsingForegroundBackgroundCacheSizeLimit()) {
return;
}
for (const auto& rvh : entry.render_view_hosts()) {
RenderViewHostNoLongerStoredInternal(&*rvh);
}
}
void BackForwardCacheImpl::DestroyEvictedFrames() {
TRACE_EVENT0("navigation", "BackForwardCache::DestroyEvictedFrames");
if (entries_.empty()) {
return;
}
std::erase_if(entries_, [this](std::unique_ptr<Entry>& entry) {
if (entry->render_frame_host()->is_evicted_from_back_forward_cache()) {
if (prioritized_entry_->get() == entry.get()) {
prioritized_entry_ = entries_.end();
}
RemoveProcessesForEntry(*entry);
return true;
}
return false;
});
#if BUILDFLAG(ARKWEB_BFCACHE)
LOG(DEBUG) << "[BFCACHE] " << __func__ << " Now stored entries number is: " << GetStoredEntriesNumber();
#endif
}
bool BackForwardCacheImpl::IsAllowed(const GURL& current_url) {
return IsHostPathAllowed(current_url) && IsQueryAllowed(current_url);
}
bool BackForwardCacheImpl::IsHostPathAllowed(const GURL& current_url) {
const auto& it = blocked_urls_.find(current_url.GetHost());
if (it != blocked_urls_.end()) {
for (const std::string& blocked_path : it->second) {
if (base::StartsWith(current_url.path(), blocked_path)) {
return false;
}
}
}
if (allowed_urls_.empty()) {
return true;
}
const auto& entry = allowed_urls_.find(current_url.GetHost());
if (entry != allowed_urls_.end()) {
for (const std::string& allowed_path : entry->second) {
if (base::StartsWith(current_url.path(), allowed_path)) {
return true;
}
}
}
return false;
}
bool BackForwardCacheImpl::IsQueryAllowed(const GURL& current_url) {
std::vector<std::string> cgi_params =
base::SplitString(current_url.query(), "&", base::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
for (const std::string& cgi_param : cgi_params) {
if (base::Contains(blocked_cgi_params_, cgi_param)) {
return false;
}
}
return true;
}
void BackForwardCacheImpl::WillCommitNavigationToCachedEntry(
Entry& bfcache_entry,
base::OnceClosure done_callback) {
auto cb = base::BarrierClosure(
bfcache_entry.render_view_hosts().size(),
base::BindOnce([](base::OnceClosure cb) { std::move(cb).Run(); },
std::move(done_callback)));
for (const auto& rvh : bfcache_entry.render_view_hosts()) {
rvh->PrepareToLeaveBackForwardCache(cb);
}
}
bool BackForwardCacheImpl::
IsRenderFrameHostWithSIGInBackForwardCacheForDebugging(
SiteInstanceGroupId site_instance_group_id) {
bool found = false;
for (std::unique_ptr<Entry>& entry : entries_) {
if (entry->render_frame_host()->is_evicted_from_back_forward_cache()) {
continue;
}
entry->render_frame_host()->ForEachRenderFrameHostImplWithAction(
[&found, site_instance_group_id](RenderFrameHostImpl* rfh) {
if (rfh->GetSiteInstance()->group()->GetId() ==
site_instance_group_id) {
found = true;
return RenderFrameHost::FrameIterationAction::kStop;
}
return RenderFrameHost::FrameIterationAction::kContinue;
});
}
return found;
}
bool BackForwardCacheImpl::IsRelatedSiteInstanceInBackForwardCacheForDebugging(
SiteInstance& site_instance) {
for (std::unique_ptr<Entry>& entry : entries_) {
if (!entry->render_frame_host()->is_evicted_from_back_forward_cache() &&
entry->render_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
&site_instance)) {
return true;
}
}
return false;
}
bool BackForwardCacheImpl::
IsRenderFrameProxyHostWithSIGInBackForwardCacheForDebugging(
SiteInstanceGroupId site_instance_group_id) {
for (std::unique_ptr<Entry>& entry : entries_) {
for (const auto& entry_rfph : entry->proxy_hosts()) {
if (!entry->render_frame_host()->is_evicted_from_back_forward_cache() &&
entry_rfph.second->site_instance_group()->GetId() ==
site_instance_group_id) {
return true;
}
}
}
return false;
}
bool BackForwardCacheImpl::
IsRenderViewHostWithMapIdInBackForwardCacheForDebugging(
const RenderViewHostImpl& rvh) {
for (std::unique_ptr<Entry>& entry : entries_) {
for (const auto& entry_rvh : entry->render_view_hosts()) {
if (!entry->render_frame_host()->is_evicted_from_back_forward_cache() &&
entry_rvh->rvh_map_id() == rvh.rvh_map_id()) {
return true;
}
}
}
return false;
}
bool BackForwardCacheImpl::IsUnloadAllowed() {
#if BUILDFLAG(ARKWEB_BFCACHE)
if (base::ohos::IsPcDevice())
return false;
#endif
return base::FeatureList::IsEnabled(kBackForwardCacheUnloadAllowed);
}
void BackForwardCacheImpl::VlogUnexpectedRendererToBrowserMessage(
const char* interface_name,
uint32_t message_name,
RenderFrameHostImpl* rfh) {
VLOG(1) << "BackForwardCacheMessageFilter::WillDispatch bad_message "
<< "interface_name " << interface_name << " message_name "
<< message_name;
PageLifecycleStateManager* page_lifecycle_state_manager =
rfh->render_view_host()->GetPageLifecycleStateManager();
VLOG(1) << "URL: " << rfh->GetLastCommittedURL() << " current "
<< page_lifecycle_state_manager->IsInBackForwardCache() << " acked "
<< page_lifecycle_state_manager->last_acknowledged_state()
.is_in_back_forward_cache;
}
BackForwardCache::DisabledReason::DisabledReason(
content::BackForwardCache::DisabledSource source,
content::BackForwardCache::DisabledReasonType id,
std::string description,
std::string context,
std::string report_string)
: source(source),
id(id),
description(description),
context(context),
report_string(report_string) {}
BackForwardCache::DisabledReason::DisabledReason(
const BackForwardCache::DisabledReason& reason) = default;
std::weak_ordering BackForwardCache::DisabledReason::operator<=>(
const DisabledReason& other) const {
return std::tie(source, id) <=> std::tie(other.source, other.id);
}
bool BackForwardCache::DisabledReason::operator==(
const DisabledReason& other) const {
return std::tie(source, id) == std::tie(other.source, other.id);
}
BackForwardCacheCanStoreTreeResult::BackForwardCacheCanStoreTreeResult(
RenderFrameHostImpl* rfh,
const url::Origin& main_document_origin,
const GURL& url,
BackForwardCacheCanStoreDocumentResult& result_for_this_document)
: document_result_(std::move(result_for_this_document)),
is_same_origin_(IsSameOriginForTreeResult(rfh, main_document_origin)),
is_root_outermost_main_frame_(!rfh->GetParentOrOuterDocumentOrEmbedder()),
id_(rfh->frame_tree_node()->html_id()),
name_(rfh->frame_tree_node()->html_name()),
src_(rfh->frame_tree_node()->html_src()),
url_(url) {}
BackForwardCacheCanStoreTreeResult::BackForwardCacheCanStoreTreeResult(
bool is_same_origin,
const GURL& url)
: is_same_origin_(is_same_origin),
is_root_outermost_main_frame_(true),
id_(""),
name_(""),
src_(""),
url_(url) {}
BackForwardCacheCanStoreTreeResult::~BackForwardCacheCanStoreTreeResult() =
default;
void BackForwardCacheCanStoreTreeResult::AddReasonsToSubtreeRootFrom(
const BackForwardCacheCanStoreDocumentResult& result) {
document_result_.AddReasonsFrom(result);
}
void BackForwardCacheCanStoreTreeResult::AppendChild(
std::unique_ptr<BackForwardCacheCanStoreTreeResult> child) {
children_.push_back(std::move(child));
}
const BackForwardCacheCanStoreDocumentResult
BackForwardCacheCanStoreTreeResult::FlattenTree() {
BackForwardCacheCanStoreDocumentResult document_result;
FlattenTreeHelper(&document_result);
return document_result;
}
void BackForwardCacheCanStoreTreeResult::FlattenTreeHelper(
BackForwardCacheCanStoreDocumentResult* document_result) {
document_result->AddReasonsFrom(document_result_);
for (const auto& subtree : GetChildren()) {
subtree->FlattenTreeHelper(document_result);
}
}
std::unique_ptr<BackForwardCacheCanStoreTreeResult>
BackForwardCacheCanStoreTreeResult::CreateEmptyTreeForNavigation(
NavigationRequest* navigation) {
DCHECK(BackForwardCacheMetrics::IsCrossDocumentMainFrameHistoryNavigation(
navigation));
std::unique_ptr<BackForwardCacheCanStoreTreeResult> empty_tree(
new BackForwardCacheCanStoreTreeResult(
true, navigation->GetURL()));
return empty_tree;
}
std::unique_ptr<BackForwardCacheCanStoreTreeResult>
BackForwardCacheCanStoreTreeResult::CreateEmptyTree(RenderFrameHostImpl* rfh) {
BackForwardCacheCanStoreDocumentResult empty_result;
std::unique_ptr<BackForwardCacheCanStoreTreeResult> empty_tree(
new BackForwardCacheCanStoreTreeResult(rfh, rfh->GetLastCommittedOrigin(),
rfh->GetLastCommittedURL(),
empty_result));
return empty_tree;
}
blink::mojom::BackForwardCacheNotRestoredReasonsPtr
BackForwardCacheCanStoreTreeResult::GetWebExposedNotRestoredReasons() {
DCHECK(is_root_outermost_main_frame_);
uint32_t count = GetCrossOriginReachableFrameCount();
int exposed_cross_origin_iframe_index =
count == 0 ? 0 : base::RandInt(0, count - 1);
return GetWebExposedNotRestoredReasonsInternal(
exposed_cross_origin_iframe_index);
}
blink::mojom::BackForwardCacheNotRestoredReasonsPtr
BackForwardCacheCanStoreTreeResult::GetWebExposedNotRestoredReasonsInternal(
int& exposed_cross_origin_iframe_index) {
blink::mojom::BackForwardCacheNotRestoredReasonsPtr not_restored_reasons =
blink::mojom::BackForwardCacheNotRestoredReasons::New();
if (IsSameOrigin()) {
not_restored_reasons->same_origin_details =
blink::mojom::SameOriginBfcacheNotRestoredDetails::New();
not_restored_reasons->same_origin_details->url = url_;
auto& map = GetDocumentResult().reason_to_source_map();
for (const auto& [reason, sources] : map) {
if (base::FeatureList::IsEnabled(
blink::features::kBackForwardCacheUpdateNotRestoredReasonsName) &&
reason == "session-restored") {
return nullptr;
}
if (sources.empty()) {
not_restored_reasons->reasons.push_back(
blink::mojom::BFCacheBlockingDetailedReason::New(
reason, nullptr));
} else {
for (const auto& source : sources) {
not_restored_reasons->reasons.push_back(
blink::mojom::BFCacheBlockingDetailedReason::New(reason,
source.Clone()));
}
}
}
if (is_root_outermost_main_frame_) {
int index_copy = exposed_cross_origin_iframe_index;
bool no_masked_reason =
std::find_if(
not_restored_reasons->reasons.begin(),
not_restored_reasons->reasons.end(),
[](const blink::mojom::BFCacheBlockingDetailedReasonPtr& reason) {
return reason->name == "masked";
}) == not_restored_reasons->reasons.end();
if (HasUnexposedCrossOriginBlockingIframe(index_copy) &&
no_masked_reason) {
blink::mojom::BFCacheBlockingDetailedReasonPtr masked_reason =
blink::mojom::BFCacheBlockingDetailedReason::New();
masked_reason->name = "masked";
not_restored_reasons->reasons.push_back(std::move(masked_reason));
}
}
for (const auto& subtree : GetChildren()) {
not_restored_reasons->same_origin_details->children.push_back(
subtree->GetWebExposedNotRestoredReasonsInternal(
exposed_cross_origin_iframe_index));
}
} else {
if (!FlattenTree().CanRestore() && exposed_cross_origin_iframe_index == 0 &&
base::FeatureList::IsEnabled(kAllowCrossOriginNotRestoredReasons)) {
blink::mojom::BFCacheBlockingDetailedReasonPtr masked_reason =
blink::mojom::BFCacheBlockingDetailedReason::New();
masked_reason->name = "masked";
not_restored_reasons->reasons.push_back(std::move(masked_reason));
}
exposed_cross_origin_iframe_index--;
}
not_restored_reasons->src = src_;
not_restored_reasons->id = id_;
not_restored_reasons->name = name_;
return not_restored_reasons;
}
bool BackForwardCacheCanStoreTreeResult::HasUnexposedCrossOriginBlockingIframe(
int& exposed_cross_origin_iframe_index) {
if (!IsSameOrigin()) {
bool randomly_selected =
exposed_cross_origin_iframe_index == 0 &&
base::FeatureList::IsEnabled(kAllowCrossOriginNotRestoredReasons);
exposed_cross_origin_iframe_index--;
if (!FlattenTree().CanRestore() && !randomly_selected) {
return true;
} else {
return false;
}
} else {
for (const auto& subtree : GetChildren()) {
if (subtree->HasUnexposedCrossOriginBlockingIframe(
exposed_cross_origin_iframe_index)) {
return true;
}
}
return false;
}
}
uint32_t
BackForwardCacheCanStoreTreeResult::GetCrossOriginReachableFrameCount() {
if (!IsSameOrigin()) {
return 1;
}
uint32_t count = 0;
for (const auto& subtree : GetChildren()) {
count += subtree->GetCrossOriginReachableFrameCount();
}
return count;
}
BackForwardCacheCanStoreDocumentResultWithTree::
BackForwardCacheCanStoreDocumentResultWithTree(
BackForwardCacheCanStoreDocumentResult& flattened_reasons,
std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_reasons)
: flattened_reasons(std::move(flattened_reasons)),
tree_reasons(std::move(tree_reasons)) {}
BackForwardCacheCanStoreDocumentResultWithTree::
BackForwardCacheCanStoreDocumentResultWithTree(
BackForwardCacheCanStoreDocumentResultWithTree&& other)
: flattened_reasons(other.flattened_reasons),
tree_reasons(std::move(other.tree_reasons)) {}
BackForwardCacheCanStoreDocumentResultWithTree::
~BackForwardCacheCanStoreDocumentResultWithTree() = default;
}