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 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_NO_STATE_PREFETCH_BROWSER_NO_STATE_PREFETCH_MANAGER_H_
#define COMPONENTS_NO_STATE_PREFETCH_BROWSER_NO_STATE_PREFETCH_MANAGER_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <set>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_config.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_histograms.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_manager_delegate.h"
#include "components/no_state_prefetch/common/no_state_prefetch_final_status.h"
#include "components/no_state_prefetch/common/no_state_prefetch_origin.h"
#include "content/public/browser/preloading_data.h"
#include "content/public/browser/render_process_host_observer.h"
#include "third_party/blink/public/mojom/prerender/prerender.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace base {
class TickClock;
} // namespace base
namespace chrome_browser_net {
enum class NetworkPredictionStatus;
}
namespace content {
class WebContents;
class BrowserContext;
} // namespace content
namespace gfx {
class Rect;
class Size;
} // namespace gfx
namespace prerender {
namespace test_utils {
class PrerenderInProcessBrowserTest;
}
class NoStatePrefetchHandle;
class NoStatePrefetchHistory;
// Observer interface for NoStatePrefetchManager events.
class NoStatePrefetchManagerObserver {
public:
virtual ~NoStatePrefetchManagerObserver();
// Called from the UI thread.
virtual void OnFirstContentfulPaint() = 0;
};
// NoStatePrefetchManager is responsible for initiating and keeping prerendered
// views of web pages. All methods must be called on the UI thread unless
// indicated otherwise.
class NoStatePrefetchManager : public content::RenderProcessHostObserver,
public KeyedService {
public:
// One or more of these flags must be passed to ClearData() to specify just
// what data to clear. See function declaration for more information.
enum ClearFlags {
CLEAR_PRERENDER_CONTENTS = 0x1 << 0,
CLEAR_PRERENDER_HISTORY = 0x1 << 1,
CLEAR_MAX = 0x1 << 2
};
// Owned by a BrowserContext object for the lifetime of the browser_context.
NoStatePrefetchManager(
content::BrowserContext* browser_context,
std::unique_ptr<NoStatePrefetchManagerDelegate> delegate);
NoStatePrefetchManager(const NoStatePrefetchManager&) = delete;
NoStatePrefetchManager& operator=(const NoStatePrefetchManager&) = delete;
~NoStatePrefetchManager() override;
// From KeyedService:
void Shutdown() override;
// Entry points for adding prerenders.
// Starts a prefetch for |url| if valid. |process_id| and |route_id| identify
// the RenderView that the prefetch request came from. If |size| is empty, a
// default from the NoStatePrefetchConfig is used. Returns a
// NoStatePrefetchHandle if the URL was added, NULL if it was not. If the
// launching RenderView is itself prefetching, the prefetch is added as a
// pending prefetch.
std::unique_ptr<NoStatePrefetchHandle> StartPrefetchingFromLinkRelPrerender(
int process_id,
int route_id,
const GURL& url,
blink::mojom::PrerenderTriggerType trigger_type,
const content::Referrer& referrer,
const url::Origin& initiator_origin,
const gfx::Size& size);
// Adds a NoStatePrefetch that only allows for same origin requests (i.e.,
// requests that only redirect to the same origin).
std::unique_ptr<NoStatePrefetchHandle> AddSameOriginSpeculation(
const GURL& url,
content::SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size,
const url::Origin& initiator_origin);
// Cancels all active prerenders.
void CancelAllPrerenders();
// Destroys all pending prerenders using FinalStatus. Also deletes them as
// well as any swapped out WebContents queued for destruction.
// Used both on destruction, and when clearing the browsing history.
void DestroyAllContents(FinalStatus final_status);
// Moves a NoStatePrefetchContents to the pending delete list from the list of
// active prerenders when prerendering should be cancelled.
virtual void MoveEntryToPendingDelete(NoStatePrefetchContents* entry,
FinalStatus final_status);
// Query the list of current prefetches to see if the given web contents is
// prefetching a page.
bool IsWebContentsPrefetching(const content::WebContents* web_contents) const;
// Returns the NoStatePrefetchContents object for the given web_contents,
// otherwise returns NULL. Note that the NoStatePrefetchContents may have been
// Destroy()ed, but not yet deleted.
NoStatePrefetchContents* GetNoStatePrefetchContents(
const content::WebContents* web_contents) const;
// Returns the NoStatePrefetchContents object for a given child_id, route_id
// pair, otherwise returns NULL. Note that the NoStatePrefetchContents may
// have been Destroy()ed, but not yet deleted.
virtual NoStatePrefetchContents* GetNoStatePrefetchContentsForRoute(
int child_id,
int route_id) const;
// Returns a list of all WebContents being NoStatePrefetched.
std::vector<content::WebContents*>
GetAllNoStatePrefetchingContentsForTesting() const;
// Checks whether |url| has been recently navigated to.
bool HasRecentlyBeenNavigatedTo(Origin origin, const GURL& url);
// Returns a `base::Value::Dict` object containing the active pages being
// prerendered, and a history of pages which were prerendered.
base::Value::Dict CopyAsDict() const;
// Clears the data indicated by which bits of clear_flags are set.
//
// If the CLEAR_PRERENDER_CONTENTS bit is set, all active prerenders are
// cancelled and then deleted, and any WebContents queued for destruction are
// destroyed as well.
//
// If the CLEAR_PRERENDER_HISTORY bit is set, the prerender history is
// cleared, including any entries newly created by destroying them in
// response to the CLEAR_PRERENDER_CONTENTS flag.
//
// Intended to be used when clearing the cache or history.
void ClearData(int clear_flags);
// Record a final status of a prerendered page in a histogram.
void RecordFinalStatus(Origin origin, FinalStatus final_status) const;
const NoStatePrefetchConfig& config() const { return config_; }
NoStatePrefetchConfig& mutable_config() { return config_; }
// Records that some visible tab navigated (or was redirected) to the
// provided URL.
void RecordNavigation(const GURL& url);
// Return current time and ticks with ability to mock the clock out for
// testing.
base::Time GetCurrentTime() const;
base::TimeTicks GetCurrentTimeTicks() const;
void SetTickClockForTesting(const base::TickClock* tick_clock);
void AddObserver(std::unique_ptr<NoStatePrefetchManagerObserver> observer);
// Registers a new ProcessHost performing a prerender. Called by
// NoStatePrefetchContents.
void AddPrerenderProcessHost(content::RenderProcessHost* process_host);
// Returns whether or not |process_host| may be reused for new navigations
// from a prerendering perspective. Currently, if Prerender Cookie Stores are
// enabled, prerenders must be in their own processes that may not be shared.
bool MayReuseProcessHost(content::RenderProcessHost* process_host);
// content::RenderProcessHostObserver implementation.
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
// Cleans up the expired prefetches and then returns true if |url| was
// no-state prefetched recently. If so, |prefetch_age|, |final_status| and
// |origin| are set based on the no-state prefetch information if they are
// non-null.
bool GetPrefetchInformation(const GURL& url,
base::TimeDelta* prefetch_age,
FinalStatus* final_status,
Origin* origin);
void SetNoStatePrefetchContentsFactoryForTest(
NoStatePrefetchContents::Factory* no_state_prefetch_contents_factory);
base::WeakPtr<NoStatePrefetchManager> AsWeakPtr();
// Clears the list of recently prefetched URLs. Allows, for example, to reuse
// the same URL in tests, without running into FINAL_STATUS_DUPLICATE.
void ClearPrefetchInformationForTesting();
// Starts a prefetch for |url| from |initiator_origin|. The |origin| specifies
// how the prefetch was started. Returns a NoStatePrefetchHandle or nullptr.
// Only for testing.
std::unique_ptr<NoStatePrefetchHandle>
StartPrefetchingWithPreconnectFallbackForTesting(
Origin origin,
const GURL& url,
const std::optional<url::Origin>& initiator_origin);
protected:
class NoStatePrefetchData {
public:
struct OrderByExpiryTime;
NoStatePrefetchData(NoStatePrefetchManager* manager,
std::unique_ptr<NoStatePrefetchContents> contents,
base::TimeTicks expiry_time);
NoStatePrefetchData(const NoStatePrefetchData&) = delete;
NoStatePrefetchData& operator=(const NoStatePrefetchData&) = delete;
~NoStatePrefetchData();
// A new NoStatePrefetchHandle has been created for this
// NoStatePrefetchData.
void OnHandleCreated(NoStatePrefetchHandle* handle);
// The launcher associated with a handle is navigating away from the context
// that launched this prefetch. If the prerender is active, it may stay
// alive briefly though, in case we we going through a redirect chain that
// will eventually land at it.
void OnHandleNavigatedAway(NoStatePrefetchHandle* handle);
// The launcher associated with a handle has taken explicit action to cancel
// this prefetch. We may well destroy the prerender in this case if no other
// handles continue to track it.
void OnHandleCanceled(NoStatePrefetchHandle* handle);
NoStatePrefetchContents* contents() { return contents_.get(); }
std::unique_ptr<NoStatePrefetchContents> ReleaseContents();
int handle_count() const { return handle_count_; }
base::TimeTicks expiry_time() const { return expiry_time_; }
void set_expiry_time(base::TimeTicks expiry_time) {
expiry_time_ = expiry_time;
}
base::WeakPtr<NoStatePrefetchData> AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
const raw_ptr<NoStatePrefetchManager> manager_;
std::unique_ptr<NoStatePrefetchContents> contents_;
// The number of distinct NoStatePrefetchHandles created for |this|,
// including ones that have called
// NoStatePrefetchData::OnHandleNavigatedAway(), but not counting the ones
// that have called NoStatePrefetchData::OnHandleCanceled(). For pending
// prefetches, this will always be 1, since the NoStatePrefetchManager only
// merges handles of running prefetches.
int handle_count_ = 0;
// After this time, this prefetch is no longer fresh, and should be removed.
base::TimeTicks expiry_time_;
base::WeakPtrFactory<NoStatePrefetchData> weak_factory_{this};
};
// Called by a NoStatePrefetchData to signal that the launcher has navigated
// away from the context that launched the prefetch. A user may have clicked
// a link in a page containing a <link rel=prerender> element, or the user
// might have committed an omnibox navigation. This is used to possibly
// shorten the TTL of the page for NoStatePrefetch.
void SourceNavigatedAway(NoStatePrefetchData* prefetch_data);
// Same as base::SysInfo::IsLowEndDevice(), overridden in tests.
virtual bool IsLowEndDevice() const;
// Whether network prediction is enabled for prefetch origin, |origin|.
bool IsPredictionEnabled(Origin origin);
private:
friend class test_utils::PrerenderInProcessBrowserTest;
friend class NoStatePrefetchContents;
friend class NoStatePrefetchHandle;
friend class UnitTestNoStatePrefetchManager;
class OnCloseWebContentsDeleter;
struct NavigationRecord;
using NoStatePrefetchDataVector =
std::vector<std::unique_ptr<NoStatePrefetchData>>;
// Time interval before a new prefetch is allowed.
static const int kMinTimeBetweenPrefetchesMs = 500;
// Time window for which we record old navigations, in milliseconds.
static const int kNavigationRecordWindowMs = 5000;
// Starts a prefetch for |url| from |referrer|. The |origin| specifies how the
// prefetch was started. If |bounds| is empty, then
// NoStatePrefetchContents::StartPrerendering will instead use a default from
// NoStatePrefetchConfig. Returns a NoStatePrefetchHandle or NULL.
// PreloadingAttempt helps us to log various metrics associated with
// particular NoStatePrefetch attempt.
// TODO(crbug.com/40238653): Remove nullptr as default parameter once NSP is
// integrated with all different predictors.
std::unique_ptr<NoStatePrefetchHandle> StartPrefetchingWithPreconnectFallback(
Origin origin,
const GURL& url,
const content::Referrer& referrer,
const std::optional<url::Origin>& initiator_origin,
const gfx::Rect& bounds,
content::SessionStorageNamespace* session_storage_namespace,
base::WeakPtr<content::PreloadingAttempt> attempt = nullptr);
void StartSchedulingPeriodicCleanups();
void StopSchedulingPeriodicCleanups();
// Deletes stale and cancelled prerendered NoStatePrefetchContents, as well as
// WebContents that have been replaced by prerendered WebContents.
// Also identifies and kills NoStatePrefetchContents that use too much
// resources.
void PeriodicCleanup();
// Posts a task to call PeriodicCleanup. Results in quicker destruction of
// objects. If |this| is deleted before the task is run, the task will
// automatically be cancelled.
void PostCleanupTask();
base::TimeTicks GetExpiryTimeForNewPrerender(Origin origin) const;
base::TimeTicks GetExpiryTimeForNavigatedAwayPrerender() const;
void DeleteOldEntries();
void DeleteToDeletePrerenders();
// Virtual so unit tests can override this.
virtual std::unique_ptr<NoStatePrefetchContents>
CreateNoStatePrefetchContents(
const GURL& url,
const content::Referrer& referrer,
const std::optional<url::Origin>& initiator_origin,
Origin origin);
// Insures the |active_prefetches_| are sorted by increasing expiry time. Call
// after every mutation of |active_prefetches_| that can possibly make it
// unsorted (e.g. an insert, or changing an expiry time).
void SortActivePrefetches();
// Finds the active NoStatePrefetchData object for a running prefetch matching
// |url| and |session_storage_namespace|.
NoStatePrefetchData* FindNoStatePrefetchData(
const GURL& url,
content::SessionStorageNamespace* session_storage_namespace);
// Given the |no_state_prefetch_contents|, find the iterator in
// |active_prefetches_| corresponding to the given prefetch.
NoStatePrefetchDataVector::iterator FindIteratorForNoStatePrefetchContents(
NoStatePrefetchContents* no_state_prefetch_contents);
bool DoesRateLimitAllowPrefetch(Origin origin) const;
// Deletes old WebContents that have been replaced by prerendered ones. This
// is needed because they're replaced in a callback from the old WebContents,
// so cannot immediately be deleted.
void DeleteOldWebContents();
// Called when NoStatePrefetchContents gets destroyed. Attaches the
// |final_status| to the most recent prefetch matching the |url|.
void SetPrefetchFinalStatusForUrl(const GURL& url, FinalStatus final_status);
// Called when a prefetch has been used. Prefetches avoid cache revalidation
// only once.
void OnPrefetchUsed(const GURL& url);
// Cleans up old NavigationRecord's.
void CleanUpOldNavigations(std::vector<NavigationRecord>* navigations,
base::TimeDelta max_age);
// Arrange for the given WebContents to be deleted asap. Delete |deleter| as
// well.
void ScheduleDeleteOldWebContents(std::unique_ptr<content::WebContents> tab,
OnCloseWebContentsDeleter* deleter);
// Adds to the history list.
void AddToHistory(NoStatePrefetchContents* contents);
// Returns a new `base::Value::List` representing the pages currently being
// prerendered.
base::Value::List GetActivePrerenders() const;
// Records the final status a prerender in the case that a
// NoStatePrefetchContents was never created, adds a NoStatePrefetchHistory
// entry, and may also initiate a preconnect to |url|.
void SkipNoStatePrefetchContentsAndMaybePreconnect(
const GURL& url,
Origin origin,
FinalStatus final_status) const;
// May initiate a preconnect to |url_arg| based on |origin|.
void MaybePreconnect(Origin origin, const GURL& url_arg) const;
// The configuration.
NoStatePrefetchConfig config_;
// The browser_context that owns this NoStatePrefetchManager.
raw_ptr<content::BrowserContext> browser_context_;
// The delegate that allows content embedder to override the logic in this
// class.
std::unique_ptr<NoStatePrefetchManagerDelegate> delegate_;
// All running prefetches. Sorted by expiry time, in ascending order.
NoStatePrefetchDataVector active_prefetches_;
// Prefetches awaiting deletion.
NoStatePrefetchDataVector to_delete_prefetches_;
// List of recent navigations in this browser_context, sorted by ascending
// |navigate_time_|.
std::vector<NavigationRecord> navigations_;
// List of recent prefetches, sorted by ascending navigate time.
std::vector<NavigationRecord> prefetches_;
std::unique_ptr<NoStatePrefetchContents::Factory>
no_state_prefetch_contents_factory_;
// RepeatingTimer to perform periodic cleanups of pending prerendered
// pages.
base::RepeatingTimer repeating_timer_;
// Track time of last prefetch to limit prefetch spam.
base::TimeTicks last_prefetch_start_time_;
std::vector<std::unique_ptr<content::WebContents>> old_web_contents_list_;
std::vector<std::unique_ptr<OnCloseWebContentsDeleter>>
on_close_web_contents_deleters_;
const std::unique_ptr<NoStatePrefetchHistory> prefetch_history_;
const std::unique_ptr<NoStatePrefetchHistograms> histograms_;
// Set of process hosts being prerendered.
using PrerenderProcessSet =
std::set<raw_ptr<content::RenderProcessHost, SetExperimental>>;
PrerenderProcessSet prerender_process_hosts_;
raw_ptr<const base::TickClock> tick_clock_;
std::vector<std::unique_ptr<NoStatePrefetchManagerObserver>> observers_;
base::WeakPtrFactory<NoStatePrefetchManager> weak_factory_{this};
};
} // namespace prerender
#endif // COMPONENTS_NO_STATE_PREFETCH_BROWSER_NO_STATE_PREFETCH_MANAGER_H_
|