blob: 29aee13e1dd676150218a745055cac54b0821164 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_
#define CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/predictors/loading_data_collector.h"
#include "chrome/browser/predictors/predictors_traffic_annotations.h"
#include "chrome/browser/predictors/prefetch_manager.h"
#include "chrome/browser/predictors/resource_prefetch_predictor.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/preconnect_manager.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
#include "url/origin.h"
class Profile;
namespace features {
BASE_DECLARE_FEATURE(kSuppressesLoadingPredictorOnSlowNetwork);
extern const base::FeatureParam<base::TimeDelta>
kSuppressesLoadingPredictorOnSlowNetworkThreshold;
} // namespace features
namespace predictors {
class ResourcePrefetchPredictor;
class LoadingStatsCollector;
class PrewarmHttpDiskCacheManager;
// Entry point for the Loading predictor.
// From a high-level request (GURL and motivation) and a database of historical
// data, initiates predictive actions to speed up page loads.
//
// Also listens to main frame requests/redirects/responses to initiate and
// cancel page load hints.
//
// See ResourcePrefetchPredictor for a description of the resource prefetch
// predictor.
//
// All methods must be called from the UI thread.
class LoadingPredictor : public KeyedService,
public content::PreconnectManager::Delegate,
public PrefetchManager::Delegate {
public:
LoadingPredictor(const LoadingPredictorConfig& config, Profile* profile);
LoadingPredictor(const LoadingPredictor&) = delete;
LoadingPredictor& operator=(const LoadingPredictor&) = delete;
~LoadingPredictor() override;
// Hints that a page load is expected for |url|, with the hint coming from a
// given |origin|. If |preconnect_prediction| is provided, this will use it
// over local predictions to trigger actions, such as prefetch and/or
// preconnect. Returns true if no more preconnect actions should be taken by
// the caller.
bool PrepareForPageLoad(
const std::optional<url::Origin>& initiator_origin,
const GURL& url,
HintOrigin origin,
bool preconnectable = false,
std::optional<PreconnectPrediction> preconnect_prediction = std::nullopt);
// Indicates that a page load hint is no longer active.
void CancelPageLoadHint(const GURL& url);
// Starts initialization, will complete asynchronously.
void StartInitialization();
// Don't use, internal only.
ResourcePrefetchPredictor* resource_prefetch_predictor();
LoadingDataCollector* loading_data_collector();
content::PreconnectManager* preconnect_manager();
PrefetchManager* prefetch_manager();
// KeyedService:
void Shutdown() override;
bool WasShutdown() { return shutdown_; }
// OnNavigationStarted is invoked when navigation |navigation_id| with
// |main_frame_url| has started navigating.
void OnNavigationStarted(NavigationId navigation_id,
ukm::SourceId ukm_source_id,
const GURL& main_frame_url,
base::TimeTicks creation_time);
void OnNavigationFinished(NavigationId navigation_id,
const GURL& old_main_frame_url,
const GURL& new_main_frame_url,
bool is_error_page);
base::WeakPtr<LoadingPredictor> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// content::PreconnectManager::Delegate:
void PreconnectInitiated(const GURL& url,
const GURL& preconnect_url) override;
void PreconnectFinished(
std::unique_ptr<content::PreconnectStats> stats) override;
bool IsPreconnectEnabled() override;
// PrefetchManager::Delegate:
void PrefetchInitiated(const GURL& url, const GURL& prefetch_url) override;
void PrefetchFinished(std::unique_ptr<PrefetchStats> stats) override;
size_t GetActiveHintsSizeForTesting() { return active_hints_.size(); }
size_t GetTotalHintsActivatedForTesting() { return total_hints_activated_; }
size_t GetActiveNavigationsSizeForTesting() {
return active_navigations_.size();
}
const std::map<GURL, base::TimeTicks>& active_hints_for_testing() const {
return active_hints_;
}
// May start a preconnect for `url`, if the current profile settings allow to
// perform preresolve and preconnect actions. When `traffic_annotation` is
// set, it will use the value over the default
// `kLoadingPredictorPreconnectTrafficAnnotation`, later passed on to //net.
// Virtual for testing.
virtual void PreconnectURLIfAllowed(
const GURL& url,
bool allow_credentials,
const net::NetworkAnonymizationKey& network_anonymization_key,
const net::NetworkTrafficAnnotationTag& traffic_annotation =
kLoadingPredictorPreconnectTrafficAnnotation,
const content::StoragePartitionConfig* storage_partition_config =
nullptr);
void MaybePrewarmResources(const std::optional<url::Origin>& initiator_origin,
const GURL& top_frame_main_resource_url);
bool IsLCPPTestingEnabled() const { return is_lcpp_testing_enabled_; }
private:
// Stores the information necessary to keep track of the active navigations.
struct NavigationInfo {
GURL main_frame_url;
base::TimeTicks creation_time;
};
struct PreconnectData {
url::Origin last_origin_;
base::TimeTicks last_preconnect_time_;
base::TimeTicks last_preresolve_time_;
};
// Cancels an active hint, from its iterator inside |active_hints_|. If the
// iterator is .end(), does nothing. Returns the iterator after deletion of
// the entry.
std::map<GURL, base::TimeTicks>::iterator CancelActiveHint(
std::map<GURL, base::TimeTicks>::iterator hint_it);
void CleanupAbandonedHintsAndNavigations(NavigationId navigation_id);
// May start preconnect and preresolve jobs according to `prediction` for
// `url`.
//
// When LoadingPredictorPrefetch is enabled, starts prefetch jobs if
// `prediction` has prefetch requests.
void MaybeAddPreconnect(const GURL& url, PreconnectPrediction prediction);
// If a preconnect or prefetch exists for `url`, stop it.
void MaybeRemovePreconnect(const GURL& url);
// May start a preconnect or a preresolve for `url`. `preconnectable`
// indicates if preconnect is possible, or only preresolve will be performed.
bool HandleHintByOrigin(const GURL& url,
bool preconnectable,
bool only_allow_https,
PreconnectData& preconnect_data);
// For testing.
void set_mock_resource_prefetch_predictor(
std::unique_ptr<ResourcePrefetchPredictor> predictor) {
resource_prefetch_predictor_ = std::move(predictor);
}
// For testing.
void set_mock_preconnect_manager(
std::unique_ptr<content::PreconnectManager> preconnect_manager) {
preconnect_manager_ = std::move(preconnect_manager);
}
// For testing.
void set_mock_loading_data_collector(
std::unique_ptr<LoadingDataCollector> loading_data_collector) {
loading_data_collector_ = std::move(loading_data_collector);
}
// For LCPP testing.
void EnableLCPPTesting() { is_lcpp_testing_enabled_ = true; }
LoadingPredictorConfig config_;
raw_ptr<Profile, DanglingUntriaged> profile_;
std::unique_ptr<ResourcePrefetchPredictor> resource_prefetch_predictor_;
std::unique_ptr<LoadingStatsCollector> stats_collector_;
std::unique_ptr<LoadingDataCollector> loading_data_collector_;
std::unique_ptr<content::PreconnectManager> preconnect_manager_;
std::unique_ptr<PrefetchManager> prefetch_manager_;
std::unique_ptr<PrewarmHttpDiskCacheManager> prewarm_http_disk_cache_manager_;
std::map<GURL, base::TimeTicks> active_hints_;
std::map<NavigationId, NavigationInfo> active_navigations_;
std::map<GURL, std::set<NavigationId>> active_urls_to_navigations_;
bool shutdown_ = false;
size_t total_hints_activated_ = 0;
PreconnectData omnibox_preconnect_data_;
PreconnectData bookmark_bar_preconnect_data_;
PreconnectData new_tab_page_preconnect_data_;
bool is_lcpp_testing_enabled_ = false;
friend class LoadingPredictorTest;
friend class LoadingPredictorPreconnectTest;
friend class LoadingPredictorTabHelperTest;
friend class LoadingPredictorTabHelperTestCollectorTest;
friend class LCPPTimingPredictorTestBase;
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestMainFrameResponseCancelsHint);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestMainFrameRequestCancelsStaleNavigations);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestMainFrameResponseClearsNavigations);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestMainFrameRequestDoesntCancelExternalHint);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestDuplicateHintAfterPreconnectCompleteCalled);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestDuplicateHintAfterPreconnectCompleteNotCalled);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
TestDontTrackNonPrefetchableUrls);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest, TestDontPredictOmniboxHints);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorPreconnectTest,
TestHandleHintWithOpaqueOrigins);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorPreconnectTest,
TestHandleHintWhenOnlyHttpsAllowed);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorPreconnectTest,
TestHandleHintPreresolveWhenOnlyHttpsAllowed);
base::WeakPtrFactory<LoadingPredictor> weak_factory_{this};
};
} // namespace predictors
#endif // CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_