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
content / browser / preloading / preloading_decider.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_PRELOADING_PRELOADING_DECIDER_H_
#define CONTENT_BROWSER_PRELOADING_PRELOADING_DECIDER_H_
#include "content/browser/preloading/preconnector.h"
#include "content/browser/preloading/prefetcher.h"
#include "content/browser/preloading/preloading_confidence.h"
#include "content/browser/preloading/prerenderer.h"
#include "content/public/browser/document_user_data.h"
#include "third_party/blink/public/mojom/preloading/anchor_element_interaction_host.mojom-forward.h"
namespace content {
class RenderFrameHost;
class PreloadingPredictor;
class PreloadingDeciderObserverForTesting {
public:
virtual ~PreloadingDeciderObserverForTesting() = default;
virtual void UpdateSpeculationCandidates(
const std::vector<blink::mojom::SpeculationCandidatePtr>& candidates) = 0;
virtual void OnPointerDown(const GURL& url) = 0;
virtual void OnPointerHover(const GURL& url) = 0;
};
// Processes user interaction events and developer provided speculation-rules
// and based on some heuristics decides which preloading actions are safe and
// worth executing.
// TODO(isaboori): implement the preloading link selection heuristics logic
class CONTENT_EXPORT PreloadingDecider
: public DocumentUserData<PreloadingDecider> {
public:
using SpeculationCandidateKey =
std::pair<GURL, blink::mojom::SpeculationAction>;
~PreloadingDecider() override;
// Receives and processes on pointer down event for 'url' target link.
void OnPointerDown(const GURL& url);
// Receives and processes on pointer hover event for 'url' target link.
void OnPointerHover(const GURL& url,
blink::mojom::AnchorElementPointerDataPtr mouse_data);
// Receives and processes ML model score for 'url' target link.
void OnPreloadingHeuristicsModelDone(const GURL& url, float score);
// Sets the new preloading decider observer for testing and returns the old
// one.
PreloadingDeciderObserverForTesting* SetObserverForTesting(
PreloadingDeciderObserverForTesting* observer);
// Returns subcomponents for testing.
Prefetcher& GetPrefetcherForTesting() { return prefetcher_; }
Prerenderer& GetPrerendererForTesting();
// Sets the new prerenderer for testing and returns the old one.
std::unique_ptr<Prerenderer> SetPrerendererForTesting(
std::unique_ptr<Prerenderer> prerenderer);
// Processes the received speculation rules candidates list.
void UpdateSpeculationCandidates(
std::vector<blink::mojom::SpeculationCandidatePtr>& candidates);
// Called when LCP is predicted.
// This is used to defer starting prerenders until LCP timing and is only
// used under LCPTimingPredictorPrerender2.
void OnLCPPredicted();
// Returns true if the |url|, |action| pair is in the on-standby list.
bool IsOnStandByForTesting(const GURL& url,
blink::mojom::SpeculationAction action) const;
// Returns true if there are any candidates.
bool HasCandidatesForTesting() const;
// Called by PrefetchService/PrerendererImpl when a prefetch/prerender is
// evicted/canceled.
void OnPreloadDiscarded(const SpeculationCandidateKey key);
private:
explicit PreloadingDecider(RenderFrameHost* rfh);
friend class DocumentUserData<PreloadingDecider>;
DOCUMENT_USER_DATA_KEY_DECL();
// Attempts preloading actions starting from the most advanced (prerendering)
// to least (preconnect), in response to `enacting_predictor` predicting a
// navigation to `url`. If `fallback_to_preconnect` is true, we preconnect if
// no other action is taken.
void MaybeEnactCandidate(const GURL& url,
const PreloadingPredictor& enacting_predictor,
PreloadingConfidence confidence,
bool fallback_to_preconnect);
// Prefetches the |url| if it is safe and eligible to be prefetched. Returns
// false if no suitable (given |enacting_predictor|) on-standby candidate is
// found for the given |url|, or the Prefetcher does not accept the candidate.
bool MaybePrefetch(const GURL& url,
const PreloadingPredictor& enacting_predictor,
PreloadingConfidence confidence);
// Returns true if a prefetch was attempted for the |url| and is not failed or
// discarded by Prefetcher yet, and we should wait for it to finish.
bool ShouldWaitForPrefetchResult(const GURL& url);
// Prerenders the |url| if it is safe and eligible to be prerendered. Returns
// false for the first bool if no suitable (given |enacting_predictor|)
// on-standby candidate is found for the given |url|, or the Prerenderer does
// not accept the candidate. Returns true for the second bool if a
// PreloadingPrediction has been added.
std::pair<bool, bool> MaybePrerender(
const GURL& url,
const PreloadingPredictor& enacting_predictor,
PreloadingConfidence confidence);
// Returns true if a prerender was attempted for the |url| and is not failed
// or discarded by Prerenderer yet, and we should wait for it to finish.
bool ShouldWaitForPrerenderResult(const GURL& url);
// Helper function to add a preloading prediction for the |url|
void AddPreloadingPrediction(const GURL& url,
PreloadingPredictor predictor,
PreloadingConfidence confidence);
// Return true if |candidate| can be selected in response to a prediction by
// |predictor|.
bool IsSuitableCandidate(
const blink::mojom::SpeculationCandidatePtr& candidate,
const PreloadingPredictor& predictor,
PreloadingConfidence confidence,
blink::mojom::SpeculationAction action) const;
// Helper functions to add/remove a preloading candidate to
// |on_standby_candidates_| and to reset |on_standby_candidates_|. Use these
// methods to make sure |on_standby_candidates_| and
// |no_vary_search_hint_on_standby_candidates_| are kept in sync
void AddStandbyCandidate(
const blink::mojom::SpeculationCandidatePtr& candidate);
void RemoveStandbyCandidate(const SpeculationCandidateKey key);
void ClearStandbyCandidates();
// Helper functions to select a prerender/prefetch candidate to be
// triggered.
std::optional<
std::pair<SpeculationCandidateKey, blink::mojom::SpeculationCandidatePtr>>
GetMatchedPreloadingCandidate(const SpeculationCandidateKey& lookup_key,
const PreloadingPredictor& enacting_predictor,
PreloadingConfidence confidence) const;
std::optional<
std::pair<SpeculationCandidateKey, blink::mojom::SpeculationCandidatePtr>>
GetMatchedPreloadingCandidateByNoVarySearchHint(
const SpeculationCandidateKey& lookup_key,
const PreloadingPredictor& enacting_predictor,
PreloadingConfidence confidence) const;
// |on_standby_candidates_| stores preloading candidates for each target URL,
// action pairs that are safe to perform but are not marked as |kEager| and
// should be performed when we are confident enough that the user will most
// likely navigate to the target URL.
std::map<SpeculationCandidateKey,
std::vector<blink::mojom::SpeculationCandidatePtr>>
on_standby_candidates_;
// |nvs_hint_on_standby_candidates_| stores for a URL without query and
// fragment, action pairs that are safe to perform but are not marked as
// |kEager| and should be performed when we are confident enough that the user
// will most likely navigate to a URL that matches based on the presence
// of No-Vary-Search hint the candidate's URL.
// This map needs to be kept in sync with the |on_standby_candidates_| map.
std::map<SpeculationCandidateKey, std::set<SpeculationCandidateKey>>
no_vary_search_hint_on_standby_candidates_;
// |processed_candidates_| stores all target URL, action pairs that are
// already processed by prefetcher or prerenderer, and maps them to all
// candidates with the same URL, action pair. Right now it is needed to avoid
// adding such candidates back to |on_standby_candidates_| whenever there is
// an update in speculation rules.
std::map<SpeculationCandidateKey,
std::vector<blink::mojom::SpeculationCandidatePtr>>
processed_candidates_;
// Behavior determined dynamically. Stored on this object rather than globally
// so that it does not span unit tests.
class BehaviorConfig;
std::unique_ptr<const BehaviorConfig> behavior_config_;
// Whether this page has ever received an ML model prediction. Once it has,
// the model predictions supersede the hover heuristic. We store this here,
// rather than per-BrowserContext, since even if the model is loaded, it may
// not run for some pages (e.g. insecure http).
bool ml_model_available_ = false;
raw_ptr<PreloadingDeciderObserverForTesting> observer_for_testing_;
Preconnector preconnector_;
Prefetcher prefetcher_;
std::unique_ptr<Prerenderer> prerenderer_;
};
} // namespace content
#endif // CONTENT_BROWSER_PRELOADING_PRELOADING_DECIDER_H_