blob: 5339c834796abff13b075d96ce2df20a0b2ea5f7 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_IMPL_H_
#define CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_IMPL_H_
#include <list>
#include <memory>
#include <set>
#include <unordered_map>
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/back_forward_cache_can_store_document_result.h"
#include "content/browser/renderer_host/render_process_host_internal_observer.h"
#include "content/common/content_export.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/common/content_features.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/mojom/page/page.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
#include "url/gurl.h"
namespace content {
class RenderFrameHostImpl;
class RenderFrameProxyHost;
class RenderViewHostImpl;
class SiteInstance;
// This feature is used to limit the scope of back-forward cache experiment
// without enabling it. To control the URLs list by using this feature by
// generating the metrics only for "allowed_websites" param. Mainly, to ensure
// that metrics from the control and experiment groups are consistent.
constexpr base::Feature kRecordBackForwardCacheMetricsWithoutEnabling{
"RecordBackForwardCacheMetricsWithoutEnabling",
base::FEATURE_DISABLED_BY_DEFAULT};
// BackForwardCache:
//
// After the user navigates away from a document, the old one goes into the
// frozen state and is kept in this object. They can potentially be reused
// after an history navigation. Reusing a document means swapping it back with
// the current_frame_host.
class CONTENT_EXPORT BackForwardCacheImpl
: public BackForwardCache,
public RenderProcessHostInternalObserver {
public:
enum MessageHandlingPolicyWhenCached {
kMessagePolicyNone,
kMessagePolicyLog,
kMessagePolicyDump,
};
static MessageHandlingPolicyWhenCached
GetChannelAssociatedMessageHandlingPolicy();
struct CONTENT_EXPORT Entry {
using RenderFrameProxyHostMap =
std::unordered_map<int32_t /* SiteInstance ID */,
std::unique_ptr<RenderFrameProxyHost>>;
Entry(std::unique_ptr<RenderFrameHostImpl> rfh,
RenderFrameProxyHostMap proxy_hosts,
std::set<RenderViewHostImpl*> render_view_hosts);
~Entry();
void WriteIntoTrace(perfetto::TracedValue context);
// Indicates whether or not all the |render_view_hosts| in this entry have
// received the acknowledgement from renderer that it finished running
// handlers.
bool AllRenderViewHostsReceivedAckFromRenderer();
// The main document being stored.
std::unique_ptr<RenderFrameHostImpl> render_frame_host;
// Proxies of the main document as seen by other processes.
// Currently, we only store proxies for SiteInstances of all subframes on
// the page, because pages using window.open and nested WebContents are not
// cached.
RenderFrameProxyHostMap proxy_hosts;
// RenderViewHosts belonging to the main frame, and its proxies (if any).
//
// While RenderViewHostImpl(s) are in the BackForwardCache, they aren't
// reused for pages outside the cache. This prevents us from having two main
// frames, (one in the cache, one live), associated with a single
// RenderViewHost.
//
// Keeping these here also prevents RenderFrameHostManager code from
// unwittingly iterating over RenderViewHostImpls that are in the cache.
std::set<RenderViewHostImpl*> render_view_hosts;
// Additional parameters to send with SetPageLifecycleState calls when we're
// restoring a page from the back-forward cache.
blink::mojom::PageRestoreParamsPtr page_restore_params;
DISALLOW_COPY_AND_ASSIGN(Entry);
};
// Returns whether MediaSessionImpl::OnServiceCreated is allowed for the
// BackForwardCache.
static bool IsMediaSessionImplOnServiceCreatedAllowed();
BackForwardCacheImpl();
~BackForwardCacheImpl() override;
// Returns whether a RenderFrameHost can be stored into the BackForwardCache
// right now. Depends on the |render_frame_host| and its children's state.
// Should only be called after we've navigated away from |render_frame_host|,
// which means nothing about the page can change (usage of blocklisted
// features, pending navigations, load state, etc.) anymore.
BackForwardCacheCanStoreDocumentResult CanStorePageNow(
RenderFrameHostImpl* render_frame_host);
// Whether a RenderFrameHost could be stored into the BackForwardCache at some
// point in the future. Different than CanStorePageNow() above, we won't check
// for properties of |render_frame_host| that might change in the future such
// as usage of certain APIs, loading state, existence of pending navigation
// requests, etc. This should be treated as a "best guess" on whether a page
// still has a chance to be stored in the back-forward cache later on, and
// should not be used as a final check before storing a page to the
// back-forward cache (for that, use CanStorePageNow() instead).
BackForwardCacheCanStoreDocumentResult CanPotentiallyStorePageLater(
RenderFrameHostImpl* render_frame_host);
// Moves the specified BackForwardCache entry into the BackForwardCache. It
// can be reused in a future history navigation by using RestoreEntry(). When
// the BackForwardCache is full, the least recently used document is evicted.
// Precondition: CanStoreDocument(*(entry->render_frame_host)).
void StoreEntry(std::unique_ptr<Entry> entry);
// Ensures that the cache is within its size limits. This should be called
// whenever events occur that could put the cache outside its limits. What
// those events are depends on the cache limit policy.
void EnforceCacheSizeLimit();
// Returns a pointer to a cached BackForwardCache entry matching
// |navigation_entry_id| if it exists in the BackForwardCache. Returns nullptr
// if no matching entry is found.
//
// Note: The returned pointer should be used temporarily only within the
// execution of a single task on the event loop. Beyond that, there is no
// guarantee the pointer will be valid, because the document may be
// removed/evicted from the cache.
Entry* GetEntry(int navigation_entry_id);
// During a history navigation, moves an entry out of the BackForwardCache
// knowing its |navigation_entry_id|. |page_restore_params| includes
// information that is needed by the entry's page after getting restored,
// which includes the latest history information (offset, length) and the
// timestamp corresponding to the start of the back-forward cached navigation,
// which would be communicated to the page to allow it to record the latency
// of this navigation.
std::unique_ptr<Entry> RestoreEntry(
int navigation_entry_id,
blink::mojom::PageRestoreParamsPtr page_restore_params);
// Evict all cached pages in the same BrowsingInstance as
// |site_instance|.
void EvictFramesInRelatedSiteInstances(SiteInstance* site_instance);
// Immediately deletes all frames in the cache. This should only be called
// when WebContents is being destroyed.
void Shutdown();
// Posts a task to destroy all frames in the BackForwardCache that have been
// marked as evicted.
void PostTaskToDestroyEvictedFrames();
// Storing frames in back-forward cache is not supported indefinitely
// due to potential privacy issues and memory leaks. Instead we are evicting
// the frame from the cache after the time to live, which can be controlled
// via experiment.
static base::TimeDelta GetTimeToLiveInBackForwardCache();
// Gets the maximum number of entries the BackForwardCache can hold per tab.
static size_t GetCacheSize();
// The back-forward cache is experimented on a limited set of URLs. This
// method returns true if the |url| matches one of those. URL not matching
// this won't enter the back-forward cache.
// This is controlled by GetAllowedURLs method which depends on the
// following:
// - feature::kBackForwardCache param -> allowed_websites.
// - kRecordBackForwardCacheMetricsWithoutEnabling param -> allowed_websites.
// If no param is set all websites are allowed by default. This can still
// return true even when BackForwardCache is disabled for metrics purposes.
bool IsAllowed(const GURL& current_url);
// This is a wrapper around the flag that indicates whether or not the
// feature usage should be checked only after receiving an ack from the
// renderer process to ensure that the features cleaned up in pagehide and
// other event handlers are acoounted for.
// TODO(crbug.com/1129331): Remove this when we implement the logic to
// consider cache size limit.
bool CheckFeatureUsageOnlyAfterAck();
// Called just before commit for a navigation that's served out of the back
// forward cache. This method will disable eviction in renderers and invoke
// |done_callback| when they are ready for the navigation to be committed.
void WillCommitNavigationToCachedEntry(Entry& bfcache_entry,
base::OnceClosure done_callback);
// Returns the task runner that should be used by the eviction timer.
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
return task_runner_for_testing_ ? task_runner_for_testing_
: base::ThreadTaskRunnerHandle::Get();
}
// Inject task runner for precise timing control in browser tests.
void SetTaskRunnerForTesting(
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
task_runner_for_testing_ = task_runner;
}
const std::list<std::unique_ptr<Entry>>& GetEntries();
// BackForwardCache overrides:
void Flush() override;
void DisableForTesting(DisableForTestingReason reason) override;
// RenderProcessHostInternalObserver methods
void RenderProcessBackgroundedChanged(RenderProcessHostImpl* host) override;
// Returns true if we are managing the cache size using foreground and
// background limits (if finch parameter "foreground_cache_size" > 0).
static bool UsingForegroundBackgroundCacheSizeLimit();
private:
// Destroys all evicted frames in the BackForwardCache.
void DestroyEvictedFrames();
// Helper for recursively checking each child's usage of blocklisted features.
// See CanStorePageNow() and CanPotentiallyStorePageLater().
void CheckDynamicBlocklistedFeaturesOnSubtree(
BackForwardCacheCanStoreDocumentResult* result,
RenderFrameHostImpl* render_frame_host);
void CanStoreRenderFrameHostLater(
BackForwardCacheCanStoreDocumentResult* result,
RenderFrameHostImpl* render_frame_host);
// If non-zero, the cache may contain at most this many entries with involving
// foregrounded processes and the remaining space can only be used by entries
// with no foregrounded processes. We can be less strict on memory usage of
// background processes because Android will kill the process if memory
// becomes scarce.
static size_t GetForegroundedEntriesCacheSize();
// Enforces a limit on the number of entries. Which entries are counted
// towards the limit depends on the values of |foregrounded_only|. If it's
// true it only considers entries that are associated with a foregrounded
// process. Otherwise all entries are considered.
size_t EnforceCacheSizeLimitInternal(size_t limit, bool foregrounded_only);
// Updates |process_to_entry_map_| with processes from |entry|. These must
// be called after adding or removing an entry in |entries_|.
void AddProcessesForEntry(Entry& entry);
void RemoveProcessesForEntry(Entry& entry);
// Contains the set of stored Entries.
// Invariant:
// - Ordered from the most recently used to the last recently used.
// - Once the list is full, the least recently used document is evicted.
std::list<std::unique_ptr<Entry>> entries_;
// Keeps track of the observed RenderProcessHosts. This is populated
// from and kept in sync with |entries_|. The RenderProcessHosts are collected
// from each Entry's RenderViewHosts. Every RenderProcessHost in here is
// observed by |this|. Every RenderProcessHost in this is referenced by a
// RenderViewHost in the Entry and so will be valid.
std::multiset<RenderProcessHost*> observed_processes_;
// Only used in tests. Whether the BackforwardCached has been disabled for
// testing.
bool is_disabled_for_testing_ = false;
// Only used for tests. This task runner is used for precise injection in
// browser tests and for timing control.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_testing_;
// To enter the back-forward cache, the main document URL's must match one of
// the field trial parameter "allowed_websites". This is represented here by a
// set of host and path prefix. When |allowed_urls_| is empty, it means there
// are no restrictions on URLs.
std::map<std::string, // URL's host,
std::vector<std::string> // URL's path prefix
>
allowed_urls_;
// This is an emergency kill switch per url to stop BFCache. The data will be
// provided via the field trial parameter "blocked_websites".
// "blocked_websites" have priority over "allowed_websites". This is
// represented here by a set of host and path prefix.
std::map<std::string, // URL's host,
std::vector<std::string> // URL's path prefix
>
blocked_urls_;
base::WeakPtrFactory<BackForwardCacheImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(BackForwardCacheImpl);
};
// Allow external code to be notified when back-forward cache is disabled for a
// RenderFrameHost. This should be used only by the testing infrastructure which
// want to know the exact reason why the cache was disabled. There can be only
// one observer.
class CONTENT_EXPORT BackForwardCacheTestDelegate {
public:
BackForwardCacheTestDelegate();
virtual ~BackForwardCacheTestDelegate();
virtual void OnDisabledForFrameWithReason(
GlobalFrameRoutingId id,
BackForwardCache::DisabledReason reason) = 0;
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_IMPL_H_