1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
content / browser / renderer_host / navigation_transitions / navigation_entry_screenshot_cache.h [blame]
// Copyright 2023 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_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_CACHE_H_
#define CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_CACHE_H_
#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
#include "base/memory/safe_ref.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_manager.h"
#include "content/common/content_export.h"
namespace content {
class NavigationEntry;
class NavigationEntryScreenshot;
class NavigationEntryScreenshotManager;
// This interface limits the access of the `NavigationEntryScreenshotManager` to
// the `NavigationEntryScreenshotCache`: we do not want the manager to
// accidentally perform any "Set" or "Take" operations on the cache. This is
// because the manager is owned by the `BrowserContext` who has access across
// the tabs, and we do not want any tab specific pixel data leaked across tabs.
class NavigationEntryScreenshotCacheEvictor {
public:
virtual ~NavigationEntryScreenshotCacheEvictor() = default;
// Starting from the most distant entry of the last committed entry, erase the
// screenshots from entries until either the memory watermark is below the
// budget, or until no screenshots are tracked in this cache (this cache is
// empty).
virtual void EvictScreenshotsUntilUnderBudgetOrEmpty() = 0;
// Deletes all the tracked screenshots in this cache, and notifies the global
// manager to stop tracking this cache.
enum class PurgeReason { kMemoryPressure, kInvisible };
virtual void Purge(PurgeReason reason) = 0;
virtual bool IsEmpty() const = 0;
// Returns the time when this cache's tab was last visible or null if it is
// currently visible.
virtual std::optional<base::TimeTicks> GetLastVisibleTime() const = 0;
};
// `NavigationEntryScreenshotCache` tracks `NavigationEntryScreenshot`s per
// `FrameTree`. It is owned by the `NavigationController` of the primary
// `FrameTree` of a `WebContents`.
class CONTENT_EXPORT NavigationEntryScreenshotCache
: public NavigationEntryScreenshotCacheEvictor {
public:
using CompressedCallback = base::OnceCallback<void(int nav_entry_index)>;
static void SetCompressedCallbackForTesting(CompressedCallback callback);
explicit NavigationEntryScreenshotCache(
base::SafeRef<NavigationEntryScreenshotManager> manager,
NavigationControllerImpl* nav_controller);
NavigationEntryScreenshotCache(const NavigationEntryScreenshotCache&) =
delete;
NavigationEntryScreenshotCache& operator=(
const NavigationEntryScreenshotCache&) = delete;
~NavigationEntryScreenshotCache() override;
// Used to assign a `NavigationEntryScreenshot` to a `NavigationEntry`, which
// will own it. Also tracks the screenshot within this cache and notifies the
// `NavigationEntryScreenshotManager` of size changes in case eviction is
// needed.
void SetScreenshot(base::WeakPtr<NavigationRequest> navigation_request,
std::unique_ptr<NavigationEntryScreenshot> screenshot,
bool is_copied_from_embedder);
// Removes the `NavigationEntryScreenshot` from `NavigationEntry` and
// transfers ownership to the caller, updating the relevant tracking in the
// `NavigationEntryScreenshotManager`. The transfer of ownership is necessary
// so that eviction does not occur while a screenshot is in use for an
// animation. The caller is responsible for making sure `navigation_entry`
// has a screenshot.
std::unique_ptr<NavigationEntryScreenshot> RemoveScreenshot(
NavigationEntry* navigation_entry);
// Called by the `NavigationScreenshot` when the hosting navigation entry is
// deleted.
void OnNavigationEntryGone(int navigation_entry_id);
// Called by `NavigationScreenshot` when the cached screenshot has been
// compressed.
void OnScreenshotCompressed(int navigation_entry_id, size_t new_size);
// Called when a navigation request has finished.
void OnNavigationFinished(const NavigationRequest& navigation_request);
// Called when the visibility of the cache changes.
void SetVisible(bool visible);
// `NavigationEntryScreenshotCacheEvictor`:
//
// The cost of `EvictScreenshotsUntilUnderBudgetOrEmpty` and
// `PurgeForMemoryPressure` is linear with respect to the number of navigation
// entries in the primary `NavigationController`. This is because for each
// navigation entry's ID this cache tracks, we need to query
// `NavigationController` using
// `NavigationControllerImpl::GetEntryWithUniqueID`, which performs a linear
// scan on all the navigation entries.
void EvictScreenshotsUntilUnderBudgetOrEmpty() override;
void Purge(PurgeReason reason) override;
bool IsEmpty() const override;
std::optional<base::TimeTicks> GetLastVisibleTime() const override;
// Allows the browsertests to be notified when a screenshot is cached.
using NewScreenshotCachedCallbackForTesting = base::OnceCallback<void(int)>;
void SetNewScreenshotCachedCallbackForTesting(
NewScreenshotCachedCallbackForTesting callback);
private:
void SetScreenshotInternal(
std::unique_ptr<NavigationEntryScreenshot> screenshot,
bool is_copied_from_embedder);
// Helper function to differentiate between purge because of memory pressure
// and purge called by the destructor.
void PurgeInternal(std::optional<PurgeReason> reason);
// Tracks the unique IDs of the navigation entries, for which we have captured
// screenshots, and the screenshot size in bytes.
base::flat_map<int, size_t> cached_screenshots_;
// Tracks the set of screenshots for ongoing navigations. These screenshots
// are either added to `cached_screenshots_` or discarded when the navigation
// finishes.
struct PendingScreenshot {
PendingScreenshot();
PendingScreenshot(std::unique_ptr<NavigationEntryScreenshot> screenshot,
bool is_copied_from_embedder);
~PendingScreenshot();
PendingScreenshot(PendingScreenshot&& other);
PendingScreenshot& operator=(PendingScreenshot&& other);
std::unique_ptr<NavigationEntryScreenshot> screenshot;
bool is_copied_from_embedder;
};
base::flat_map<int64_t, PendingScreenshot> pending_screenshots_;
// The per-BrowserContext manager that manages the eviction. Guaranteed to
// outlive `this`.
base::SafeRef<NavigationEntryScreenshotManager> manager_;
// The `NavigationController` that owns `this`. Guaranteed to outlive `this`.
// We need the controller for cache eviction to query all the
// `NavigationEntry`. See the impl for `EvictScreenshotsUntilInBudgetOrEmpty`.
const raw_ptr<NavigationControllerImpl> nav_controller_;
// The last time this cache was visible or null if its currently visible.
std::optional<base::TimeTicks> last_visible_timestamp_;
NewScreenshotCachedCallbackForTesting new_screenshot_cached_callback_;
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_CACHE_H_