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
content / browser / renderer_host / commit_deferring_condition_runner.h [blame]
// Copyright 2021 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_COMMIT_DEFERRING_CONDITION_RUNNER_H_
#define CONTENT_BROWSER_RENDERER_HOST_COMMIT_DEFERRING_CONDITION_RUNNER_H_
#include <map>
#include <memory>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/preloading/prerender/prerender_commit_deferring_condition.h"
#include "content/common/content_export.h"
namespace content {
class NavigationRequest;
// Helper class used to defer an otherwise fully-prepared navigation (i.e.
// followed all redirects, passed all NavigationThrottle checks) from
// proceeding until all preconditions are met.
//
// Clients subclass the CommitDeferringCondition class to wait on a commit
// blocking condition to be resolved and invoke the callback when it's ready.
// The client should register their subclass class in
// RegisterDeferringConditions(). Each condition is run in order, waiting on
// that condition to call Resume() before starting the next one. Once the final
// condition is completed, the navigation is resumed to commit.
//
// This mechanism is not applied to about:blank or same-document navigations.
//
// CommitDeferringCondition vs. NavigationThrottle: At first glance this
// mechanism may seem redundant to using a NavigationThrottle (and deferring in
// WillProcessResponse). However, the behavior will differ on pages initially
// loaded into a non-primary FrameTree (e.g. prerendering or BFCached page).
// In these cases NavigationThrottles will run only when the page was loading,
// they will not get a chance to intervene when it is being activated to the
// primary FrameTree (i.e. user navigates to a prerendered page). If the
// navigation needs to be deferred during such activations, a
// CommitDeferringCondition must be used. It runs both when the navigation is
// loading and when a navigation activates into the primary FrameTree.
class CONTENT_EXPORT CommitDeferringConditionRunner {
public:
class Delegate {
public:
// Called after all conditions run. `candidate_prerender_frame_tree_node_id`
// is used for querying the PrerenderHost that this navigation will try to
// activate. See comments on `candidate_prerender_frame_tree_node_id_` for
// details.
virtual void OnCommitDeferringConditionChecksComplete(
CommitDeferringCondition::NavigationType navigation_type,
std::optional<FrameTreeNodeId>
candidate_prerender_frame_tree_node_id) = 0;
};
// Creates the runner and adds all the conditions in
// RegisterDeferringConditions. `candidate_prerender_frame_tree_node_id`
// is used for querying the PrerenderHost that this navigation will try to
// activate. See comments on `candidate_prerender_frame_tree_node_id_` for
// details.
static std::unique_ptr<CommitDeferringConditionRunner> Create(
NavigationRequest& navigation_request,
CommitDeferringCondition::NavigationType navigation_type,
std::optional<FrameTreeNodeId> candidate_prerender_frame_tree_node_id);
CommitDeferringConditionRunner(const CommitDeferringConditionRunner&) =
delete;
CommitDeferringConditionRunner& operator=(
const CommitDeferringConditionRunner&) = delete;
~CommitDeferringConditionRunner();
// Call to start iterating through registered CommitDeferringConditions. This
// calls OnCommitDeferringConditionChecksComplete on the |delegate_| when all
// conditions have been resolved. This may happen either synchronously or
// asynchronously.
void ProcessChecks();
// Call to register all deferring conditions. This should be called when
// NavigationState < WILL_START_NAVIGATION for prerendered page activation, or
// NavigationState == WILL_PROCESS_RESPONSE for other navigations.
void RegisterDeferringConditions(NavigationRequest& navigation_request);
// Installs a callback to generate a deferring condition. Installed callbacks
// are called every time RegisterDeferringConditions() is called. Generated
// conditions are added to `conditions_` and run after all regularly
// registered conditions. This is typically used for adding a condition before
// NavigationRequest is created.
using ConditionGenerator =
base::RepeatingCallback<std::unique_ptr<CommitDeferringCondition>(
NavigationHandle&,
CommitDeferringCondition::NavigationType)>;
// Specifies whether a ConditionGenerator installs its condition to run
// before existing conditions or after. Note: generators are run in the order
// in which they are added.
enum class InsertOrder { kBefore, kAfter };
// Returns a generator id that is used for uninstalling the generator.
static int InstallConditionGeneratorForTesting(ConditionGenerator generator,
InsertOrder order);
// `generator_id` should be an identifier returned by
// InstallConditionGeneratorForTesting().
static void UninstallConditionGeneratorForTesting(int generator_id);
// Used in tests to inject mock conditions.
void AddConditionForTesting(
std::unique_ptr<CommitDeferringCondition> condition);
// Returns the condition that's currently causing the navigation commit to be
// deferred. If no condition is currently deferred, returns nullptr.
CommitDeferringCondition* GetDeferringConditionForTesting() const;
private:
friend class CommitDeferringConditionRunnerTest;
CommitDeferringConditionRunner(
Delegate& delegate,
CommitDeferringCondition::NavigationType navigation_type,
std::optional<FrameTreeNodeId> candidate_prerender_frame_tree_node_id);
// Called asynchronously to resume iterating through
// CommitDeferringConditions after one has been deferred. A callback for this
// method is passed into each condition when WillCommitNavigation is called.
void ResumeProcessing();
void ProcessConditions();
void AddCondition(std::unique_ptr<CommitDeferringCondition> condition,
InsertOrder order = InsertOrder::kAfter);
std::vector<std::unique_ptr<CommitDeferringCondition>> conditions_;
// This class is owned by its delegate (the NavigationRequest) so it's safe
// to keep a reference to it.
const raw_ref<Delegate> delegate_;
// Used for distiguishing prerendered page activation from other navigations.
// This is needed as IsPageActivation() and IsPrerenderedPageActivation() on
// NavigationRequest are not available yet while CommitDeferringCondition is
// running.
const CommitDeferringCondition::NavigationType navigation_type_;
// Used for querying PrerenderHost this navigation will try to activate.
// This is valid only when `navigation_type_` is kPrerenderedPageActivation.
// This is needed as PrerenderHost hasn't been reserved and
// prerender_frame_tree_node_id() on NavigationRequest is not available yet
// while CommitDeferringCondition is running.
const std::optional<FrameTreeNodeId> candidate_prerender_frame_tree_node_id_;
// True when we're blocked waiting on a call to ResumeProcessing.
bool is_deferred_ = false;
base::WeakPtrFactory<CommitDeferringConditionRunner> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_COMMIT_DEFERRING_CONDITION_RUNNER_H_