| // 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. |
| |
| #include "chrome/browser/predictors/loading_predictor.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "chrome/browser/predictors/loading_test_util.h" |
| #include "chrome/browser/predictors/predictors_traffic_annotations.h" |
| #include "chrome/browser/preloading/preloading_prefs.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/preconnect_manager.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/preconnect_test_util.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/network_anonymization_key.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/origin.h" |
| |
| using testing::_; |
| using testing::Return; |
| using testing::StrictMock; |
| using testing::DoAll; |
| using testing::SetArgPointee; |
| |
| using content::PreconnectManager; |
| using content::PreconnectRequest; |
| using content::PreconnectStats; |
| |
| namespace predictors { |
| |
| namespace { |
| |
| // First two are preconnectable, last one is not (see SetUp()). |
| const char kUrl[] = "http://www.google.com/cats"; |
| const char kUrl2[] = "http://www.google.com/dogs"; |
| const char kUrl3[] = |
| "file://unknown.website/catsanddogs"; // Non http(s) scheme to avoid |
| // preconnect to the main frame. |
| |
| class MockPreconnectManager : public PreconnectManager { |
| public: |
| MockPreconnectManager() = default; |
| |
| MOCK_METHOD2(StartProxy, |
| void(const GURL& url, |
| const std::vector<PreconnectRequest>& requests)); |
| MOCK_METHOD4( |
| StartPreresolveHost, |
| void(const GURL& url, |
| const net::NetworkAnonymizationKey& network_anonymization_key, |
| net::NetworkTrafficAnnotationTag traffic_annotation, |
| const content::StoragePartitionConfig*)); |
| MOCK_METHOD4( |
| StartPreresolveHosts, |
| void(const std::vector<GURL>& urls, |
| const net::NetworkAnonymizationKey& network_anonymization_key, |
| net::NetworkTrafficAnnotationTag traffic_annotation, |
| const content::StoragePartitionConfig*)); |
| MOCK_METHOD7( |
| StartPreconnectUrl, |
| void(const GURL& url, |
| bool allow_credentials, |
| net::NetworkAnonymizationKey network_anonymization_key, |
| net::NetworkTrafficAnnotationTag traffic_annotation, |
| const content::StoragePartitionConfig*, |
| std::optional<net::ConnectionKeepAliveConfig> keepalive_config, |
| mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient> |
| observer_client)); |
| MOCK_METHOD1(Stop, void(const GURL& url)); |
| |
| MOCK_METHOD0(GetWeakPtr, base::WeakPtr<PreconnectManager>()); |
| MOCK_METHOD1(SetNetworkContextForTesting, |
| void(network::mojom::NetworkContext* network_context)); |
| MOCK_METHOD1(SetObserverForTesting, void(Observer* observer)); |
| |
| void Start(const GURL& url, |
| std::vector<PreconnectRequest> requests, |
| net::NetworkTrafficAnnotationTag traffic_annotation) override { |
| StartProxy(url, requests); |
| } |
| }; |
| |
| |
| LoadingPredictorConfig CreateConfig() { |
| LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| return config; |
| } |
| |
| // Creates a NetworkAnonymizationKey for a main frame navigation to URL. |
| net::NetworkAnonymizationKey CreateNetworkanonymization_key( |
| const GURL& main_frame_url) { |
| net::SchemefulSite site = net::SchemefulSite(main_frame_url); |
| return net::NetworkAnonymizationKey::CreateSameSite(site); |
| } |
| |
| NavigationId GetNextId() { |
| static NavigationId::Generator generator; |
| return generator.GenerateNextId(); |
| } |
| |
| } // namespace |
| |
| class LoadingPredictorTest : public testing::Test { |
| public: |
| ~LoadingPredictorTest() override; |
| void SetUp() override; |
| void TearDown() override; |
| |
| protected: |
| virtual void SetPreference(); |
| |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<TestingProfile> profile_; |
| std::unique_ptr<LoadingPredictor> predictor_; |
| raw_ptr<StrictMock<MockResourcePrefetchPredictor>> mock_predictor_; |
| }; |
| |
| LoadingPredictorTest::~LoadingPredictorTest() = default; |
| |
| void LoadingPredictorTest::SetUp() { |
| profile_ = std::make_unique<TestingProfile>(); |
| SetPreference(); |
| auto config = CreateConfig(); |
| predictor_ = std::make_unique<LoadingPredictor>(config, profile_.get()); |
| |
| auto mock = std::make_unique<StrictMock<MockResourcePrefetchPredictor>>( |
| config, profile_.get()); |
| EXPECT_CALL(*mock, PredictPreconnectOrigins(GURL(kUrl), _)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*mock, PredictPreconnectOrigins(GURL(kUrl2), _)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*mock, PredictPreconnectOrigins(GURL(kUrl3), _)) |
| .WillRepeatedly(Return(false)); |
| |
| mock_predictor_ = mock.get(); |
| predictor_->set_mock_resource_prefetch_predictor(std::move(mock)); |
| |
| predictor_->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| } |
| |
| void LoadingPredictorTest::TearDown() { |
| predictor_->Shutdown(); |
| } |
| |
| void LoadingPredictorTest::SetPreference() { |
| prefetch::SetPreloadPagesState(profile_->GetPrefs(), |
| prefetch::PreloadPagesState::kNoPreloading); |
| } |
| |
| class LoadingPredictorPreconnectTest : public LoadingPredictorTest { |
| public: |
| void SetUp() override; |
| void TearDown() override; |
| |
| protected: |
| void SetPreference() override; |
| |
| raw_ptr<StrictMock<MockPreconnectManager>> mock_preconnect_manager_; |
| }; |
| |
| void LoadingPredictorPreconnectTest::SetUp() { |
| LoadingPredictorTest::SetUp(); |
| auto mock_preconnect_manager = |
| std::make_unique<StrictMock<MockPreconnectManager>>(); |
| mock_preconnect_manager_ = mock_preconnect_manager.get(); |
| predictor_->set_mock_preconnect_manager(std::move(mock_preconnect_manager)); |
| } |
| |
| void LoadingPredictorPreconnectTest::TearDown() { |
| mock_preconnect_manager_ = nullptr; |
| LoadingPredictorTest::TearDown(); |
| } |
| |
| void LoadingPredictorPreconnectTest::SetPreference() { |
| prefetch::SetPreloadPagesState( |
| profile_->GetPrefs(), prefetch::PreloadPagesState::kStandardPreloading); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestOnNavigationStarted) { |
| // Should return true if there are predictions. |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, GURL(kUrl), HintOrigin::NAVIGATION)); |
| |
| // Should return false since there are no predictions. |
| EXPECT_FALSE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, GURL(kUrl3), HintOrigin::NAVIGATION)); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestMainFrameResponseCancelsHint) { |
| const GURL url = GURL(kUrl); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::EXTERNAL); |
| EXPECT_EQ(1UL, predictor_->active_hints_.size()); |
| |
| auto navigation_id = GetNextId(); |
| predictor_->OnNavigationFinished(navigation_id, url, url, false); |
| EXPECT_TRUE(predictor_->active_hints_.empty()); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestMainFrameResponseClearsNavigations) { |
| const GURL url(kUrl); |
| const GURL redirected(kUrl2); |
| const auto& active_navigations = predictor_->active_navigations_; |
| const auto& active_hints = predictor_->active_hints_; |
| const auto& active_urls_to_navigations = |
| predictor_->active_urls_to_navigations_; |
| |
| auto navigation_id = GetNextId(); |
| |
| predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(), url, |
| base::TimeTicks::Now()); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::EXTERNAL); |
| EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end()); |
| EXPECT_FALSE(active_hints.empty()); |
| EXPECT_NE(active_urls_to_navigations.find(url), |
| active_urls_to_navigations.end()); |
| |
| predictor_->OnNavigationFinished(navigation_id, url, url, false); |
| EXPECT_TRUE(active_navigations.empty()); |
| EXPECT_TRUE(active_hints.empty()); |
| EXPECT_TRUE(active_urls_to_navigations.empty()); |
| |
| // With redirects. |
| predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(), url, |
| base::TimeTicks::Now()); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::EXTERNAL); |
| EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end()); |
| EXPECT_FALSE(active_hints.empty()); |
| EXPECT_NE(active_urls_to_navigations.find(url), |
| active_urls_to_navigations.end()); |
| |
| predictor_->OnNavigationFinished(navigation_id, url, redirected, false); |
| EXPECT_TRUE(active_navigations.empty()); |
| EXPECT_TRUE(active_hints.empty()); |
| EXPECT_TRUE(active_urls_to_navigations.empty()); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestMainFrameRequestDoesntCancelExternalHint) { |
| const GURL url = GURL(kUrl); |
| const auto& active_navigations = predictor_->active_navigations_; |
| auto& active_hints = predictor_->active_hints_; |
| |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::EXTERNAL); |
| auto it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_TRUE(active_navigations.empty()); |
| |
| // To check that the hint is not replaced, set the start time in the past, |
| // and check later that it didn't change. |
| base::TimeTicks start_time = it->second - base::Seconds(10); |
| it->second = start_time; |
| |
| auto navigation_id = GetNextId(); |
| |
| predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(), |
| GURL(url.spec()), base::TimeTicks::Now()); |
| EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end()); |
| it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_EQ(start_time, it->second); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestDuplicateHintAfterPreconnectCompleteCalled) { |
| const GURL url = GURL(kUrl); |
| const auto& active_navigations = predictor_->active_navigations_; |
| auto& active_hints = predictor_->active_hints_; |
| |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::EXTERNAL); |
| auto it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_TRUE(active_navigations.empty()); |
| |
| // To check that the hint is replaced, set the start time in the past, |
| // and check later that it changed. |
| base::TimeTicks start_time = it->second - base::Seconds(10); |
| it->second = start_time; |
| |
| std::unique_ptr<PreconnectStats> preconnect_stats = |
| std::make_unique<PreconnectStats>(url); |
| predictor_->PreconnectFinished(std::move(preconnect_stats)); |
| |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::NAVIGATION_PREDICTOR); |
| it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_TRUE(active_navigations.empty()); |
| |
| // Calling PreconnectFinished() must have cleared the hint, and duplicate |
| // PrepareForPageLoad() call should be honored. |
| EXPECT_LT(start_time, it->second); |
| } |
| |
| TEST_F(LoadingPredictorTest, |
| TestDuplicateHintAfterPreconnectCompleteNotCalled) { |
| const GURL url = GURL(kUrl); |
| const auto& active_navigations = predictor_->active_navigations_; |
| auto& active_hints = predictor_->active_hints_; |
| |
| predictor_->PrepareForPageLoad(std::nullopt, url, HintOrigin::EXTERNAL); |
| auto it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_TRUE(active_navigations.empty()); |
| |
| content::RunAllTasksUntilIdle(); |
| it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| |
| // To check that the hint is not replaced, set the start time in the recent |
| // past, and check later that it didn't change. |
| base::TimeTicks start_time = it->second - base::Seconds(10); |
| it->second = start_time; |
| |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url, |
| HintOrigin::NAVIGATION_PREDICTOR); |
| it = active_hints.find(url); |
| EXPECT_NE(it, active_hints.end()); |
| EXPECT_TRUE(active_navigations.empty()); |
| |
| // Duplicate PrepareForPageLoad() call should not be honored. |
| EXPECT_EQ(start_time, it->second); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestDontTrackNonPrefetchableUrls) { |
| const GURL url3 = GURL(kUrl3); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url3, |
| HintOrigin::NAVIGATION); |
| EXPECT_TRUE(predictor_->active_hints_.empty()); |
| } |
| |
| TEST_F(LoadingPredictorTest, TestDontPredictOmniboxHints) { |
| const GURL omnibox_suggestion = GURL("http://search.com/kittens"); |
| // We expect that no prediction will be requested. |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| omnibox_suggestion, HintOrigin::OMNIBOX); |
| EXPECT_TRUE(predictor_->active_hints_.empty()); |
| } |
| |
| TEST_F(LoadingPredictorPreconnectTest, TestHandleOmniboxHint) { |
| const GURL preconnect_suggestion = GURL("http://search.com/kittens"); |
| EXPECT_CALL(*mock_preconnect_manager_, |
| StartPreconnectUrl( |
| preconnect_suggestion, true, |
| CreateNetworkanonymization_key(preconnect_suggestion), |
| kLoadingPredictorPreconnectTrafficAnnotation, _, _, _)); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| preconnect_suggestion, HintOrigin::OMNIBOX, |
| true); |
| // The second suggestion for the same host should be filtered out. |
| const GURL preconnect_suggestion2 = GURL("http://search.com/puppies"); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| preconnect_suggestion2, HintOrigin::OMNIBOX, |
| true); |
| |
| const GURL preresolve_suggestion = GURL("http://en.wikipedia.org/wiki/main"); |
| net::SchemefulSite site = net::SchemefulSite(preresolve_suggestion); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartPreresolveHost(preresolve_suggestion, |
| net::NetworkAnonymizationKey::CreateSameSite(site), |
| kLoadingPredictorPreconnectTrafficAnnotation, _)); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| preresolve_suggestion, HintOrigin::OMNIBOX, |
| false); |
| // The second suggestions should be filtered out as well. |
| const GURL preresolve_suggestion2 = |
| GURL("http://en.wikipedia.org/wiki/random"); |
| predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| preresolve_suggestion2, HintOrigin::OMNIBOX, |
| false); |
| } |
| |
| // Checks that the predictor preconnects to an initial origin even when it |
| // doesn't have any historical data for this host. |
| TEST_F(LoadingPredictorPreconnectTest, TestAddInitialUrlToEmptyPrediction) { |
| GURL main_frame_url("http://search.com/kittens"); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillOnce(Return(false)); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://search.com")), 2, |
| CreateNetworkanonymization_key(main_frame_url)}}))); |
| EXPECT_FALSE(predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| main_frame_url, |
| HintOrigin::NAVIGATION)); |
| } |
| |
| // Checks that the predictor doesn't add an initial origin to a preconnect list |
| // if the list already contains the origin. |
| TEST_F(LoadingPredictorPreconnectTest, TestAddInitialUrlMatchesPrediction) { |
| GURL main_frame_url("http://search.com/kittens"); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| PreconnectPrediction prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(prediction), Return(true))); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://search.com")), 2, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, HintOrigin::EXTERNAL)); |
| } |
| |
| // Checks that the predictor adds an initial origin to a preconnect list if the |
| // list doesn't contain this origin already. It may be possible if an initial |
| // url redirects to another host. |
| TEST_F(LoadingPredictorPreconnectTest, TestAddInitialUrlDoesntMatchPrediction) { |
| GURL main_frame_url("http://search.com/kittens"); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| PreconnectPrediction prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://en.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(prediction), Return(true))); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://search.com")), 2, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://en.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, HintOrigin::EXTERNAL)); |
| } |
| |
| // Checks that the predictor doesn't preconnect to a bad url. |
| TEST_F(LoadingPredictorPreconnectTest, TestAddInvalidInitialUrl) { |
| GURL main_frame_url("file:///tmp/index.html"); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, HintOrigin::EXTERNAL)); |
| } |
| |
| // Checks that the predictor uses the provided prediction if there isn't an |
| // active hint initiated via a local prediction happening already. |
| TEST_F(LoadingPredictorPreconnectTest, |
| TestPrepareForPageLoadPredictionProvided) { |
| GURL main_frame_url("http://search.com/kittens"); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| PreconnectPrediction prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, |
| HintOrigin::OPTIMIZATION_GUIDE, false, prediction)); |
| } |
| |
| // Checks that the predictor does not proceed with an empty request. |
| TEST_F(LoadingPredictorPreconnectTest, |
| TestPrepareForPageLoadPredictionWithEmptyRequestsProvided) { |
| GURL main_frame_url("http://nopredictions.com/"); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| PreconnectPrediction prediction; |
| EXPECT_FALSE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, |
| HintOrigin::OPTIMIZATION_GUIDE, false, prediction)); |
| } |
| |
| // Checks that the predictor preconnects to an initial origin even when it |
| // doesn't have any historical data for this host, but still allows subsequent |
| // calls to PrepareForPageLoad with a provided prediction. |
| TEST_F(LoadingPredictorPreconnectTest, |
| TestPrepareForPageLoadPreconnectsUsingPredictionWhenNoLocalPrediction) { |
| GURL main_frame_url("http://search.com/kittens"); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillRepeatedly(Return(false)); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| EXPECT_CALL(*mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://search.com")), |
| 2, network_anonymization_key}}))); |
| EXPECT_FALSE(predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, |
| main_frame_url, |
| HintOrigin::NAVIGATION)); |
| |
| // A second call to PrepareForPageLoad using a provided prediction should |
| // fire requests. |
| PreconnectPrediction prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, |
| HintOrigin::OPTIMIZATION_GUIDE, false, prediction)); |
| } |
| |
| // Checks that the predictor uses a prediction even if there is already a local |
| // initiated one in flight. |
| TEST_F( |
| LoadingPredictorPreconnectTest, |
| TestPrepareForPageLoadPredictionProvidedButHasLocalPreconnectPrediction) { |
| GURL main_frame_url("http://search.com/kittens"); |
| net::NetworkAnonymizationKey network_anonymization_key = |
| CreateNetworkanonymization_key(main_frame_url); |
| PreconnectPrediction prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}); |
| EXPECT_CALL(*mock_predictor_, PredictPreconnectOrigins(main_frame_url, _)) |
| .WillRepeatedly(DoAll(SetArgPointee<1>(prediction), Return(true))); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://search.com")), 2, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://ads.search.com")), 0, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, HintOrigin::EXTERNAL)); |
| |
| // A second call to PrepareForPageLoad using a provided prediction should not |
| // fire requests. |
| prediction = CreatePreconnectPrediction( |
| "search.com", true, |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartProxy(main_frame_url, |
| std::vector<PreconnectRequest>( |
| {{url::Origin::Create(GURL("http://cdn1.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn2.search.com")), 1, |
| network_anonymization_key}, |
| {url::Origin::Create(GURL("http://cdn3.search.com")), 1, |
| network_anonymization_key}}))); |
| EXPECT_TRUE(predictor_->PrepareForPageLoad( |
| /*initiator_origin=*/std::nullopt, main_frame_url, |
| HintOrigin::OPTIMIZATION_GUIDE, false, prediction)); |
| } |
| |
| // Checks that the opaque origins will not trigger preconnect as it is treated |
| // as cross-origin and cannot be reused. |
| TEST_F(LoadingPredictorPreconnectTest, TestHandleHintWithOpaqueOrigins) { |
| GURL main_frame_url("about:blank"); |
| LoadingPredictor::PreconnectData preconnect_data; |
| EXPECT_FALSE(predictor_->HandleHintByOrigin(main_frame_url, |
| /*preconnectable=*/true, |
| /*only_allow_https=*/false, |
| preconnect_data)); |
| } |
| |
| // Checks that the behavior of HandleHintByOrigin is expected when |
| // only_allow_https = true. |
| TEST_F(LoadingPredictorPreconnectTest, TestHandleHintWhenOnlyHttpsAllowed) { |
| GURL main_frame_url_non_https("http://www.google.com/cats"); |
| GURL main_frame_url_https("https://www.google.com/cats"); |
| LoadingPredictor::PreconnectData preconnect_data; |
| EXPECT_FALSE(predictor_->HandleHintByOrigin(main_frame_url_non_https, |
| /*preconnectable=*/true, |
| /*only_allow_https=*/true, |
| preconnect_data)); |
| EXPECT_CALL(*mock_preconnect_manager_, |
| StartPreconnectUrl( |
| main_frame_url_https, true, |
| CreateNetworkanonymization_key(main_frame_url_https), |
| kLoadingPredictorPreconnectTrafficAnnotation, _, _, _)); |
| EXPECT_TRUE(predictor_->HandleHintByOrigin(main_frame_url_https, |
| /*preconnectable=*/true, |
| /*only_allow_https=*/true, |
| preconnect_data)); |
| } |
| |
| // Checks that HandleHintByOrigin can preresolve correctly. |
| TEST_F(LoadingPredictorPreconnectTest, |
| TestHandleHintPreresolveWhenOnlyHttpsAllowed) { |
| GURL main_frame_url_non_https("http://www.google.com/cats"); |
| GURL main_frame_url_https("https://www.google.com/cats"); |
| LoadingPredictor::PreconnectData preconnect_data; |
| EXPECT_FALSE(predictor_->HandleHintByOrigin(main_frame_url_non_https, |
| /*preconnectable=*/false, |
| /*only_allow_https=*/true, |
| preconnect_data)); |
| EXPECT_CALL( |
| *mock_preconnect_manager_, |
| StartPreresolveHost(main_frame_url_https, |
| CreateNetworkanonymization_key(main_frame_url_https), |
| kLoadingPredictorPreconnectTrafficAnnotation, _)); |
| EXPECT_TRUE(predictor_->HandleHintByOrigin(main_frame_url_https, |
| /*preconnectable=*/false, |
| /*only_allow_https=*/true, |
| preconnect_data)); |
| } |
| |
| } // namespace predictors |