blob: 3c0c8d51a3bb31ed3b50a0db2cb3687591d1adef [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_BACK_FORWARD_CACHE_H_
#define CONTENT_PUBLIC_BROWSER_BACK_FORWARD_CACHE_H_
#include <cstdint>
#include <map>
#include <optional>
#include <set>
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
namespace content {
class Page;
class RenderFrameHost;
// Public API for the BackForwardCache.
//
// After the user navigates away from a document, the old one might go into the
// frozen state and will be kept in the cache. It can potentially be reused
// at a later time if the user navigates back.
//
// Not all documents can or will be cached. You should not assume a document
// will be cached.
//
// All methods of this class should be called from the UI thread.
class CONTENT_EXPORT BackForwardCache {
public:
// LINT.IfChange(NotRestoredReason)
enum class NotRestoredReason : uint8_t {
kMinValue = 0,
kNotPrimaryMainFrame = 0,
// BackForwardCache is disabled due to low memory device, base::Feature or
// command line. Note that the more specific NotRestoredReasons
// kBackForwardCacheDisabledByLowMemory and
// kBackForwardCacheDisabledByCommandLine will also be set as other reasons
// along with this when appropriate.
kBackForwardCacheDisabled = 1,
kRelatedActiveContentsExist = 2,
kHTTPStatusNotOK = 3,
kSchemeNotHTTPOrHTTPS = 4,
// DOMContentLoaded event has not yet fired. This means that deferred
// scripts have not run yet and pagehide/pageshow event handlers may not be
// installed yet.
kLoading = 5,
// 6: WasGrantedMediaAccess is no longer blocking.
kBlocklistedFeatures = 7,
kDisableForRenderFrameHostCalled = 8,
kDomainNotAllowed = 9,
kHTTPMethodNotGET = 10,
kSubframeIsNavigating = 11,
kTimeout = 12,
kCacheLimit = 13,
kJavaScriptExecution = 14,
kRendererProcessKilled = 15,
kRendererProcessCrashed = 16,
// 17: Dialogs are no longer a reason to exclude from BackForwardCache
// 18: GrantedMediaStreamAccess is no longer blocking.
// 19: kSchedulerTrackedFeatureUsed is no longer used.
kConflictingBrowsingInstance = 20,
kCacheFlushed = 21,
kServiceWorkerVersionActivation = 22,
kSessionRestored = 23,
kUnknown = 24,
kServiceWorkerPostMessage = 25,
kEnteredBackForwardCacheBeforeServiceWorkerHostAdded = 26,
// 27: kRenderFrameHostReused_SameSite was removed.
// 28: kRenderFrameHostReused_CrossSite was removed.
// 29: kNotMostRecentNavigationEntry was removed.
kServiceWorkerClaim = 30,
kIgnoreEventAndEvict = 31,
kHaveInnerContents = 32,
kTimeoutPuttingInCache = 33,
// BackForwardCache is disabled due to low memory device.
kBackForwardCacheDisabledByLowMemory = 34,
// BackForwardCache is disabled due to command-line switch (may include
// cases where the embedder disabled it due to, e.g., enterprise policy).
kBackForwardCacheDisabledByCommandLine = 35,
// 36: kFrameTreeNodeStateReset was removed.
// 37: kNetworkRequestDatapipeDrained = 37 was removed and broken into 43
// and 44.
kNetworkRequestRedirected = 38,
kNetworkRequestTimeout = 39,
kNetworkExceedsBufferLimit = 40,
kNavigationCancelledWhileRestoring = 41,
// 42: kBackForwardCacheDisabledForPrerender was removed and merged into 0.
kUserAgentOverrideDiffers = 43,
// 44: kNetworkRequestDatapipeDrainedAsDatapipe was removed now that
// ScriptStreamer is supported.
kNetworkRequestDatapipeDrainedAsBytesConsumer = 45,
kForegroundCacheLimit = 46,
kBrowsingInstanceNotSwapped = 47,
kBackForwardCacheDisabledForDelegate = 48,
// 49: kOptInUnloadHeaderNotPresent was removed as the experiments ended.
kUnloadHandlerExistsInMainFrame = 50,
kUnloadHandlerExistsInSubFrame = 51,
kServiceWorkerUnregistration = 52,
kCacheControlNoStore = 53,
kCacheControlNoStoreCookieModified = 54,
kCacheControlNoStoreHTTPOnlyCookieModified = 55,
// 56: kNoResponseHead was fixed.
// 57: kActivationNavigationsDisallowedForBug1234857 was fixed.
kErrorDocument = 58,
// 59: kFencedFramesEmbedder was removed.
kCookieDisabled = 60,
kHTTPAuthRequired = 61,
kCookieFlushed = 62,
kBroadcastChannelOnMessage = 63,
kWebViewSettingsChanged = 64,
kWebViewJavaScriptObjectChanged = 65,
kWebViewMessageListenerInjected = 66,
kWebViewSafeBrowsingAllowlistChanged = 67,
kWebViewDocumentStartJavascriptChanged = 68,
kCacheControlNoStoreDeviceBoundSessionTerminated = 69,
kCacheLimitPrunedOnModerateMemoryPressure = 70,
kCacheLimitPrunedOnCriticalMemoryPressure = 71,
kSharedWorkerMessage = 72,
kSharedWorkerWithNoActiveClient = 73,
kMaxValue = kSharedWorkerWithNoActiveClient,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:BackForwardCacheNotRestoredReason)
// Back/forward cache can be disabled from within content and also from
// embedders. This means we cannot have a unified enum that covers reasons
// from different layers. Instead we namespace the reasons and allow each
// source to manage its own enum. The previous approach was to use a hash of
// the string for logging but this made it hard identify the reasons in the
// logged data and also meant there was no control over new uses of the API.
//
// The logged value is |enum_value + source_type << 16|.
enum class DisabledSource {
// We reserve 0 because because the previous approach just used the strings
// hashed to uint16.
kLegacy = 0,
kTesting = 1,
kContent = 2,
kEmbedder = 3,
};
typedef uint16_t DisabledReasonType;
static const uint16_t kDisabledReasonTypeBits = 16;
// Represents a reason to disable back-forward cache, given by a |source|.
// |context| is arbitrary context that will be preserved and passed through,
// e.g. an extension ID responsible for disabling BFCache that can be shown in
// passed devtools. It preserves the |description| and |context| that
// accompany it, however they are ignored for <, == and !=.
struct CONTENT_EXPORT DisabledReason {
DisabledReason(BackForwardCache::DisabledSource source,
BackForwardCache::DisabledReasonType id,
std::string description,
std::string context,
std::string report_string);
DisabledReason(const DisabledReason&);
const BackForwardCache::DisabledSource source;
const BackForwardCache::DisabledReasonType id;
const std::string description;
const std::string context;
// Report string used for NotRestoredReasons API. This will be brief and
// will mask extension related reasons as "Extensions".
const std::string report_string;
std::weak_ordering operator<=>(const DisabledReason&) const;
bool operator==(const DisabledReason&) const;
};
// Prevents the `render_frame_host` from entering the BackForwardCache. A
// RenderFrameHost can only enter the BackForwardCache if the main one and all
// its children can. This action can not be undone. Any document that is
// assigned to this same RenderFrameHost in the future will not be cached
// either. In practice this is not a big deal as only navigations that use a
// new frame can be cached.
//
// This might be needed for example by components that listen to events via a
// WebContentsObserver and keep some sort of per frame state, as this state
// might be lost and not be recreated when navigating back.
//
// If the page is already in the cache an eviction is triggered.
//
// `render_frame_host`: non-null.
// `reason`: Describes who is disabling this and why.
// `source_id`: see
// `BackForwardCacheCanStoreDocumentResult::DisabledReasonsMap` for what it
// means and when it's set.
static void DisableForRenderFrameHost(
RenderFrameHost* render_frame_host,
DisabledReason reason,
std::optional<ukm::SourceId> source_id = std::nullopt);
// Helper function to be used when it is not always possible to guarantee the
// `render_frame_host` to be still alive when this is called. In this case,
// its `id` can be used.
// For what `source_id` means and when it's set, see
// `BackForwardCacheCanStoreDocumentResult::DisabledReasonsMap`.
static void DisableForRenderFrameHost(
GlobalRenderFrameHostId id,
DisabledReason reason,
std::optional<ukm::SourceId> source_id = std::nullopt);
// Helper function to be used when the input |page| has seen any form data
// associated. This state will be set on the BackForwardCacheMetrics
// associated with the main frame, is not persisted across session restores,
// and only set in Android Custom tabs for now.
// TODO(crbug.com/40251494): Set this boolean for all platforms.
static void SetHadFormDataAssociated(Page& page);
// List of reasons the BackForwardCache was disabled for a specific test. If a
// test needs to be disabled for a reason not covered below, please add to
// this enum.
enum DisableForTestingReason {
// The test has expectations that won't make sense if caching is enabled.
//
// One alternative to disabling BackForwardCache is to make the test's logic
// conditional, based on whether or not BackForwardCache is enabled.
//
// You should also consider whether it would make sense to instead split
// into two tests, one using a cacheable page, and one using an uncacheable
// page.
//
// Even though BackForwardCache is already enabled by default, it is not
// guaranteed to preserve and cache the previous document on every
// navigation, and even if it does, it is still possible for a cached
// document to get discarded without it ever getting restored, so not every
// history navigation will restore a document from the back/forward cache.
// Thus, testing cases where a document does not get preserved and cached
// on navigation or not restored on history navigation is completely valid.
TEST_REQUIRES_NO_CACHING,
// Unload events never fire for documents that are put into the
// BackForwardCache. This is by design, as there is never an appropriate
// moment to fire unload if the document is cached.
// In short, this is because:
//
// * We can't fire unload when going into the cache, because it may be
// destructive, and put the document into an unknown/bad state. Pages can
// also be cached and restored multiple times, and we don't want to invoke
// unload more than once.
//
// * We can't fire unload when the document is evicted from the cache,
// because at that point we don't want to run javascript for privacy and
// security reasons.
//
// An alternative to disabling the BackForwardCache, is to have the test
// load a page that is ineligible for caching (e.g. due to an unsupported
// feature).
TEST_USES_UNLOAD_EVENT,
// This test expects that same-site navigations won't result in a
// RenderFrameHost / RenderFrame / `blink::WebView` / RenderWidget change.
// But when same-site BackForwardCache is enabled, the change usually does
// happen. Even so, there will still be valid navigations that don't result
// in those objects changing, so we should keep the test as is, just with
// BackForwardCache disabled.
TEST_ASSUMES_NO_RENDER_FRAME_CHANGE,
};
// Returns true if BackForwardCache is enabled.
static bool IsBackForwardCacheFeatureEnabled();
// TODO(crbug.com/345117894): Add reasons to all the callsites of Flush().
// Evict all entries from the BackForwardCache with reason kCacheFlushed.
virtual void Flush() = 0;
// Evict all entries from the BackForwardCache with specific reason.
virtual void Flush(NotRestoredReason reason) = 0;
// Evict back/forward cache entries from the least recently used ones until
// the cache is within the given size limit.
// Returns the total number of BFCache entries before the pruning,
virtual size_t Prune(size_t limit, NotRestoredReason reason) = 0;
// Sets limits on cache size and time to live, which will take precedent over
// the default limits.
virtual void SetEmbedderSuppliedCacheSize(size_t cache_size) = 0;
virtual void SetEmbedderSuppliedTimeToLive(base::TimeDelta time_to_live) = 0;
// Disables the BackForwardCache so that no documents will be stored/served.
// This allows tests to "force" not using the BackForwardCache, this can be
// useful when:
// * Tests rely on a new document being loaded.
// * Tests want to test this case specifically.
// Callers should pass an accurate |reason| to make future triaging of
// disabled tests easier.
//
// Note: It's preferable to make tests BackForwardCache compatible
// when feasible, rather than using this method. Also please consider whether
// you actually should have 2 tests, one with the document cached
// (BackForwardCache enabled), and one without.
virtual void DisableForTesting(DisableForTestingReason reason) = 0;
protected:
BackForwardCache() = default;
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_BACK_FORWARD_CACHE_H_