blob: 9ed93d1500d924f6cd1ba9387ae61d1ecf7af164 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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 <string>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/predictors/loading_data_collector.h"
#include "chrome/browser/predictors/preconnect_manager.h"
#include "chrome/browser/predictors/resource_prefetch_common.h"
#include "chrome/browser/predictors/resource_prefetch_predictor.h"
#include "chrome/browser/predictors/resource_prefetcher.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/core/keyed_service.h"
#include "url/gurl.h"
class Profile;
namespace predictors {
class ResourcePrefetchPredictor;
class LoadingStatsCollector;
class TestLoadingObserver;
// 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 ResourcePrefetcher::Delegate,
public PreconnectManager::Delegate {
public:
LoadingPredictor(const LoadingPredictorConfig& config, Profile* profile);
~LoadingPredictor() override;
// Hints that a page load is expected for |url|, with the hint coming from a
// given |origin|. May trigger actions, such as prefetch and/or preconnect.
void PrepareForPageLoad(const GURL& url,
HintOrigin origin,
bool preconnectable = false);
// 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();
PreconnectManager* preconnect_manager();
// KeyedService:
void Shutdown() override;
void OnMainFrameRequest(const URLRequestSummary& summary);
void OnMainFrameRedirect(const URLRequestSummary& summary);
void OnMainFrameResponse(const URLRequestSummary& summary);
base::WeakPtr<LoadingPredictor> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// ResourcePrefetcher::Delegate:
void ResourcePrefetcherFinished(
ResourcePrefetcher* prefetcher,
std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) override;
// PreconnectManager::Delegate:
void PreconnectFinished(std::unique_ptr<PreconnectStats> stats) override;
// Sets the |observer| to be notified when prefetches start and
// finish. A previously registered observer will be discarded. Call this with
// a nullptr parameter to de-register the observer.
void SetObserverForTesting(TestLoadingObserver* observer);
private:
// 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(const NavigationID& navigation_id);
// May start a prefetch of |urls| for |url| with a given hint |origin|. A new
// prefetch may not start if there is already one in flight, for instance.
void MaybeAddPrefetch(const GURL& url,
const std::vector<GURL>& urls,
HintOrigin origin);
// If a prefetch exists for |url|, stop it.
void MaybeRemovePrefetch(const GURL& url);
// May start preconnect and preresolve jobs according to |requests| for |url|
// with a given hint |origin|.
void MaybeAddPreconnect(const GURL& url,
std::vector<PreconnectRequest>&& requests,
HintOrigin origin);
// If a preconnect 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.
void HandleOmniboxHint(const GURL& url, bool preconnectable);
// 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<PreconnectManager> preconnect_manager) {
preconnect_manager_ = std::move(preconnect_manager);
}
LoadingPredictorConfig config_;
Profile* profile_;
std::unique_ptr<ResourcePrefetchPredictor> resource_prefetch_predictor_;
std::unique_ptr<LoadingStatsCollector> stats_collector_;
std::unique_ptr<LoadingDataCollector> loading_data_collector_;
std::unique_ptr<PreconnectManager> preconnect_manager_;
std::map<GURL, base::TimeTicks> active_hints_;
// Initial URL.
std::map<NavigationID, GURL> active_navigations_;
// The value is {prefetcher, already deleted}.
std::map<std::string, std::pair<std::unique_ptr<ResourcePrefetcher>, bool>>
prefetches_;
TestLoadingObserver* observer_;
bool shutdown_ = false;
GURL last_omnibox_origin_;
base::TimeTicks last_omnibox_preconnect_time_;
base::TimeTicks last_omnibox_preresolve_time_;
friend class LoadingPredictorTest;
friend class LoadingPredictorPreconnectTest;
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,
TestDontTrackNonPrefetchableUrls);
FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest, TestDontPredictOmniboxHints);
base::WeakPtrFactory<LoadingPredictor> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(LoadingPredictor);
};
// An interface used to notify that data in the LoadingPredictor has
// changed. All methods are called on the UI thread.
class TestLoadingObserver {
public:
// De-registers itself from |predictor_| on destruction.
virtual ~TestLoadingObserver();
virtual void OnPrefetchingStarted(const GURL& main_frame_url) {}
virtual void OnPrefetchingStopped(const GURL& main_frame_url) {}
virtual void OnPrefetchingFinished(const GURL& main_frame_url) {}
protected:
// |predictor| must be non-NULL and has to outlive the LoadingTestObserver.
// Also the predictor must not have a LoadingTestObserver set.
explicit TestLoadingObserver(LoadingPredictor* predictor);
private:
LoadingPredictor* predictor_;
DISALLOW_COPY_AND_ASSIGN(TestLoadingObserver);
};
} // namespace predictors
#endif // CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_