blob: 0c87e1abf698899d77e95a527d36a77407f9cc94 [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 <set>
#include "base/strings/string_piece.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/abseil-cpp/absl/types/optional.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:
// Returns true if BackForwardCache is enabled.
static bool IsBackForwardCacheFeatureEnabled();
// 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;
bool operator<(const DisabledReason&) const;
bool 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,
absl::optional<ukm::SourceId> source_id = absl::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,
absl::optional<ukm::SourceId> source_id = absl::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/1403292): 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,
};
// Evict all entries from the BackForwardCache.
virtual void Flush() = 0;
// Evict back/forward cache entries from the least recently used ones until
// the cache is within the given size limit.
virtual void Prune(size_t limit) = 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_