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
|
// 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_
|