File: no_state_prefetch_manager.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (485 lines) | stat: -rw-r--r-- 19,151 bytes parent folder | download | duplicates (6)
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_