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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_TOP_CHROME_WEBUI_CONTENTS_PRELOAD_MANAGER_H_
#define CHROME_BROWSER_UI_WEBUI_TOP_CHROME_WEBUI_CONTENTS_PRELOAD_MANAGER_H_
#include <optional>
#include "base/no_destructor.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/browser/ui/webui/top_chrome/per_profile_webui_tracker.h"
#include "chrome/browser/ui/webui/top_chrome/preload_candidate_selector.h"
#include "chrome/browser/ui/webui/top_chrome/webui_contents_preload_state.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "url/gurl.h"
class Browser;
class PerProfileWebUITracker;
// WebUIContentsPreloadManager is a singleton class that preloads top Chrome
// WebUIs. At anytime, at most one WebContents is preloaded across all profiles.
// If under heavy memory pressure, no preloaded contents will be created.
//
// See comments in TopChromeWebUIConfig for making a WebUI preloadable.
class WebUIContentsPreloadManager : public ProfileObserver,
public PerProfileWebUITracker::Observer {
public:
enum class PreloadMode {
// Preloads on calling `WarmupForBrowser()` and after every WebUI
// creation.
// TODO(326505383): preloading on browser startup causes test failures
// primarily because they expect a certain number of WebContents are
// created.
kPreloadOnWarmup = 0,
// Preloads only after every WebUI creation.
// After the preloaded contents is taken, perloads a new contents.
kPreloadOnMakeContents = 1,
};
struct RequestResult {
RequestResult();
RequestResult(RequestResult&&);
RequestResult& operator=(RequestResult&&);
RequestResult(const RequestResult&) = delete;
RequestResult& operator=(const RequestResult&) = delete;
~RequestResult();
std::unique_ptr<content::WebContents> web_contents;
// True if `web_contents` is ready to be shown on screen. This boolean only
// reflects the state when this struct is constructed. The `web_contents`
// will cease to be ready to show, for example, if it reloads.
bool is_ready_to_show;
};
~WebUIContentsPreloadManager() override;
WebUIContentsPreloadManager(const WebUIContentsPreloadManager&) = delete;
WebUIContentsPreloadManager& operator=(const WebUIContentsPreloadManager&) =
delete;
static WebUIContentsPreloadManager* GetInstance();
// Warms up the preload manager. Depending on PreloadMode this may or may not
// make a preloaded contents.
void WarmupForBrowser(Browser* browser);
// Make a WebContents that shows `webui_url` under `browser_context`. If a
// preloaded WebContents exists for the same `browser_context`, it will be
// reused.
// This method handles navigation to `webui_url` internally.
// A new preloaded contents will be created, unless the system is under heavy
// memory pressure.
RequestResult Request(const GURL& webui_url,
content::BrowserContext* browser_context);
// Returns the timeticks when the specific `web_contents` was requested.
std::optional<base::TimeTicks> GetRequestTime(
content::WebContents* web_contents);
// Returns true if the given `web_contents` was preloaded.
bool WasPreloaded(content::WebContents* web_contents) const;
content::WebContents* preloaded_web_contents() {
return preloaded_web_contents_.get();
}
// Disable navigations for tests that don't have //content properly
// initialized.
void DisableNavigationForTesting();
private:
WebUIContentsPreloadManager();
friend class base::NoDestructor<WebUIContentsPreloadManager>;
friend class WebUIContentsPreloadManagerTestAPI;
class WebUIControllerEmbedderStub;
class PendingPreload;
// Used in telemetry to record the reason of preloading.
enum class PreloadReason {
// Preloading triggered by calling `WarmupForBrowser()`.
kBrowserWarmup = 0,
// Preloading triggered by destroy of WebUIs tracked by
// `PerProfileWebUITracker`.
kWebUIDestroyed = 1,
// Preloading triggered by request of WebUIs, i.e. `Request()` is called.
kWebUIRequested = 2,
kMaxValue = kWebUIRequested,
};
std::vector<GURL> GetAllPreloadableWebUIURLs();
// Returns the currently preloaded WebUI URL. Returns nullopt if no content is
// preloaded.
void SetPreloadCandidateSelector(
std::unique_ptr<webui::PreloadCandidateSelector>
preload_candidate_selector);
// Returns the next WebUI URL to preload. This can return nullopt indicating
// that no new WebUI will be preloaded.
std::optional<GURL> GetNextWebUIURLToPreload(
content::BrowserContext* browser_context) const;
// Preload a WebContents for `browser_context`.
// There is at most one preloaded contents at any time.
// If the preloaded contents has a different browser context, replace it
// with a new contents under the given `browser_context`.
// If under heavy memory pressure, no preloaded contents will be created.
void MaybePreloadForBrowserContext(
content::BrowserContext* browser_context,
PreloadReason preload_reason);
// Schedule a preload. This calls MaybePreloadForBrowserContext() at a later
// time.
//
// The preload will happen when `busy_web_contents_to_watch` emits the
// first non-empty paint or when the deadline has passed.
//
// When called with a nullptr `busy_web_contents_to_watch`, only watch for
// deadline to pass.
//
// When called while a preload is pending, cancel the pending preload and
// schedule a new one.
void MaybePreloadForBrowserContextLater(
content::BrowserContext* browser_context,
content::WebContents* busy_web_contents_to_watch,
PreloadReason preload_reason,
base::TimeDelta deadline = base::Seconds(3));
// Sets the current preloaded WebContents and performs necessary bookkepping.
// The bookkeeping includes monitoring for the shutdown of the browser context
// and handling the "ready-to-show" event emitted by the WebContents.
// Returns the previous preloaded WebContents.
std::unique_ptr<content::WebContents> SetPreloadedContents(
std::unique_ptr<content::WebContents> web_contents);
std::unique_ptr<content::WebContents> CreateNewContents(
content::BrowserContext* browser_context,
GURL url);
void LoadURLForContents(content::WebContents* web_contents, GURL url);
// Returns true if a new preloaded contents should be created for
// `browser_context`.
bool ShouldPreloadForBrowserContext(
content::BrowserContext* browser_context) const;
bool IsDelayPreloadEnabled() const;
// Cleans up preloaded contents on browser context shutdown.
void OnBrowserContextShutdown(content::BrowserContext* browser_context);
// ProfileObserver:
void OnProfileWillBeDestroyed(Profile* profile) override;
// PerProfileWebUITracker::Observer:
void OnWebContentsDestroyed(content::WebContents* web_contents) override;
void OnWebContentsPrimaryPageChanged(
content::WebContents* web_contents) override;
PreloadMode preload_mode_ = PreloadMode::kPreloadOnMakeContents;
// Disable navigations for views unittests because they don't initialize
// //content properly.
bool is_navigation_disabled_for_test_ = false;
// Used in tests to disable delay preload.
// If not delayed, preloading waits for non-empty paint or a deadline.
bool is_delay_preload_disabled_for_test_ = false;
// Used to prevent the preload re-entrance due to destroying the old preload
// contents.
bool is_setting_preloaded_web_contents_ = false;
std::unique_ptr<content::WebContents> preloaded_web_contents_;
std::unique_ptr<PendingPreload> pending_preload_;
// Tracks the WebUI presence state under a profile.
std::unique_ptr<PerProfileWebUITracker> webui_tracker_;
// Observes the tracker for WebContents destroy.
base::ScopedObservation<PerProfileWebUITracker,
PerProfileWebUITracker::Observer>
webui_tracker_observation_{this};
// PreloadCandidateSelector selects the next WebUI to preload.
std::unique_ptr<webui::PreloadCandidateSelector> preload_candidate_selector_;
// A stub WebUI page embdeder that captures the ready-to-show signal.
std::unique_ptr<WebUIControllerEmbedderStub> webui_controller_embedder_stub_;
// Observation of destroy of preload content's profile.
base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
};
#endif // CHROME_BROWSER_UI_WEBUI_TOP_CHROME_WEBUI_CONTENTS_PRELOAD_MANAGER_H_
|