| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/preloading/prefetch/prefetch_service.h" |
| |
| #include "base/containers/cxx20_erase.h" |
| #include "base/run_loop.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "base/time/time.h" |
| #include "base/timer/elapsed_timer.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/browser/preloading/prefetch/prefetch_container.h" |
| #include "content/browser/preloading/prefetch/prefetch_document_manager.h" |
| #include "content/browser/preloading/prefetch/prefetch_features.h" |
| #include "content/browser/preloading/prefetch/prefetch_params.h" |
| #include "content/browser/preloading/prefetch/prefetch_status.h" |
| #include "content/browser/preloading/preloading.h" |
| #include "content/browser/preloading/preloading_config.h" |
| #include "content/browser/preloading/preloading_data_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/frame_accept_header.h" |
| #include "content/public/browser/prefetch_service_delegate.h" |
| #include "content/public/browser/preloading.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/fake_service_worker_context.h" |
| #include "content/public/test/mock_navigation_handle.h" |
| #include "content/public/test/preloading_test_util.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/test/test_content_browser_client.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/proxy_server.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/parsed_headers.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/public/mojom/cookie_manager.mojom.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/public/mojom/proxy_lookup_client.mojom.h" |
| #include "services/network/test/test_network_context.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/web_preferences/web_preferences.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| namespace { |
| |
| const char kPrefetchProxyAddress[] = "https://testprefetchproxy.com"; |
| |
| const char kApiKey[] = "APIKEY"; |
| |
| const int kTotalTimeDuration = 4321; |
| |
| const int kConnectTimeDuration = 123; |
| |
| const int kHeaderLatency = 456; |
| |
| const char kHTMLMimeType[] = "text/html"; |
| |
| const char kHTMLBody[] = R"( |
| <!DOCTYPE HTML> |
| <html> |
| <head></head> |
| <body></body> |
| </html>)"; |
| |
| PreloadingEligibility ToPreloadingEligibility(PrefetchStatus status) { |
| if (status == PrefetchStatus::kPrefetchNotEligibleDataSaverEnabled) { |
| return PreloadingEligibility::kDataSaverEnabled; |
| } |
| return static_cast<PreloadingEligibility>( |
| static_cast<int>(status) + |
| static_cast<int>(PreloadingEligibility::kPreloadingEligibilityCommonEnd)); |
| } |
| |
| PreloadingFailureReason ToPreloadingFailureReason(PrefetchStatus status) { |
| return static_cast<PreloadingFailureReason>( |
| static_cast<int>(status) + |
| static_cast<int>( |
| PreloadingFailureReason::kPreloadingFailureReasonCommonEnd)); |
| } |
| |
| class MockPrefetchServiceDelegate : public PrefetchServiceDelegate { |
| public: |
| explicit MockPrefetchServiceDelegate(int num_on_prefetch_likely_calls = 1) { |
| // Sets default behavior for the delegate. |
| ON_CALL(*this, GetDefaultPrefetchProxyHost) |
| .WillByDefault(testing::Return(GURL(kPrefetchProxyAddress))); |
| ON_CALL(*this, GetAPIKey).WillByDefault(testing::Return(kApiKey)); |
| ON_CALL(*this, IsOriginOutsideRetryAfterWindow(testing::_)) |
| .WillByDefault(testing::Return(true)); |
| ON_CALL(*this, DisableDecoysBasedOnUserSettings) |
| .WillByDefault(testing::Return(false)); |
| ON_CALL(*this, IsSomePreloadingEnabled) |
| .WillByDefault(testing::Return(PreloadingEligibility::kEligible)); |
| ON_CALL(*this, IsExtendedPreloadingEnabled) |
| .WillByDefault(testing::Return(false)); |
| ON_CALL(*this, IsPreloadingPrefEnabled) |
| .WillByDefault(testing::Return(true)); |
| ON_CALL(*this, IsDataSaverEnabled).WillByDefault(testing::Return(false)); |
| ON_CALL(*this, IsBatterySaverEnabled).WillByDefault(testing::Return(false)); |
| ON_CALL(*this, IsDomainInPrefetchAllowList(testing::_)) |
| .WillByDefault(testing::Return(true)); |
| |
| EXPECT_CALL(*this, OnPrefetchLikely(testing::_)) |
| .Times(num_on_prefetch_likely_calls); |
| } |
| |
| ~MockPrefetchServiceDelegate() override = default; |
| |
| MockPrefetchServiceDelegate(const MockPrefetchServiceDelegate&) = delete; |
| MockPrefetchServiceDelegate& operator=(const MockPrefetchServiceDelegate) = |
| delete; |
| |
| // PrefetchServiceDelegate. |
| MOCK_METHOD(std::string, GetMajorVersionNumber, (), (override)); |
| MOCK_METHOD(std::string, GetAcceptLanguageHeader, (), (override)); |
| MOCK_METHOD(GURL, GetDefaultPrefetchProxyHost, (), (override)); |
| MOCK_METHOD(std::string, GetAPIKey, (), (override)); |
| MOCK_METHOD(GURL, GetDefaultDNSCanaryCheckURL, (), (override)); |
| MOCK_METHOD(GURL, GetDefaultTLSCanaryCheckURL, (), (override)); |
| MOCK_METHOD(void, |
| ReportOriginRetryAfter, |
| (const GURL&, base::TimeDelta), |
| (override)); |
| MOCK_METHOD(bool, IsOriginOutsideRetryAfterWindow, (const GURL&), (override)); |
| MOCK_METHOD(void, ClearData, (), (override)); |
| MOCK_METHOD(bool, DisableDecoysBasedOnUserSettings, (), (override)); |
| MOCK_METHOD(PreloadingEligibility, IsSomePreloadingEnabled, (), (override)); |
| MOCK_METHOD(bool, IsExtendedPreloadingEnabled, (), (override)); |
| MOCK_METHOD(bool, IsPreloadingPrefEnabled, (), (override)); |
| MOCK_METHOD(bool, IsDataSaverEnabled, (), (override)); |
| MOCK_METHOD(bool, IsBatterySaverEnabled, (), (override)); |
| MOCK_METHOD(bool, IsDomainInPrefetchAllowList, (const GURL&), (override)); |
| MOCK_METHOD(void, OnPrefetchLikely, (WebContents*), (override)); |
| }; |
| |
| class ScopedPrefetchServiceContentBrowserClient |
| : public TestContentBrowserClient { |
| public: |
| explicit ScopedPrefetchServiceContentBrowserClient( |
| std::unique_ptr<MockPrefetchServiceDelegate> |
| mock_prefetch_service_delegate) |
| : mock_prefetch_service_delegate_( |
| std::move(mock_prefetch_service_delegate)) { |
| old_browser_client_ = SetBrowserClientForTesting(this); |
| off_the_record_context_ = std::make_unique<TestBrowserContext>(); |
| off_the_record_context_->set_is_off_the_record(true); |
| } |
| |
| ~ScopedPrefetchServiceContentBrowserClient() override { |
| EXPECT_EQ(this, SetBrowserClientForTesting(old_browser_client_)); |
| } |
| |
| // ContentBrowserClient. |
| std::unique_ptr<PrefetchServiceDelegate> CreatePrefetchServiceDelegate( |
| BrowserContext*) override { |
| return std::move(mock_prefetch_service_delegate_); |
| } |
| |
| void UseOffTheRecordContextForStoragePartition(bool use) { |
| use_off_the_record_context_for_storage_paritition_ = use; |
| } |
| // `BrowserContext::GetStoragePartitionForUrl` eventually calls this method |
| // on the browser client to get the config. Overwrite it so the prefetch can |
| // be rejected due to a non-default storage partition. |
| StoragePartitionConfig GetStoragePartitionConfigForSite( |
| BrowserContext* browser_context, |
| const GURL& site) override { |
| if (use_off_the_record_context_for_storage_paritition_) { |
| return StoragePartitionConfig::CreateDefault( |
| off_the_record_context_.get()); |
| } |
| return TestContentBrowserClient::GetStoragePartitionConfigForSite( |
| browser_context, site); |
| } |
| |
| private: |
| raw_ptr<ContentBrowserClient> old_browser_client_; |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate_; |
| // This browser context is used to generate a different storage partition if |
| // `use_off_the_record_context_for_storage_paritition_` is set to true. |
| std::unique_ptr<TestBrowserContext> off_the_record_context_; |
| bool use_off_the_record_context_for_storage_paritition_{false}; |
| }; |
| |
| // This is only used to test the proxy lookup. |
| class TestNetworkContext : public network::TestNetworkContext { |
| public: |
| explicit TestNetworkContext(absl::optional<net::ProxyInfo> proxy_info) |
| : proxy_info_(proxy_info) {} |
| |
| void LookUpProxyForURL( |
| const GURL& url, |
| const net::NetworkAnonymizationKey& network_anonymization_key, |
| mojo::PendingRemote<network::mojom::ProxyLookupClient> |
| pending_proxy_lookup_client) override { |
| mojo::Remote<network::mojom::ProxyLookupClient> proxy_lookup_client( |
| std::move(pending_proxy_lookup_client)); |
| proxy_lookup_client->OnProxyLookupComplete(net::OK, proxy_info_); |
| } |
| |
| private: |
| absl::optional<net::ProxyInfo> proxy_info_; |
| }; |
| |
| class PrefetchServiceTest : public RenderViewHostTestHarness { |
| public: |
| PrefetchServiceTest() |
| : RenderViewHostTestHarness( |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME), |
| test_url_loader_factory_(/*observe_loader_requests=*/true), |
| test_shared_url_loader_factory_( |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_)) {} |
| |
| void SetUp() override { |
| RenderViewHostTestHarness::SetUp(); |
| |
| browser_context() |
| ->GetDefaultStoragePartition() |
| ->GetNetworkContext() |
| ->GetCookieManager(cookie_manager_.BindNewPipeAndPassReceiver()); |
| |
| InitScopedFeatureList(); |
| PreloadingConfig::GetInstance().ParseConfig(); |
| |
| PrefetchService::SetURLLoaderFactoryForTesting( |
| test_shared_url_loader_factory_.get()); |
| |
| PrefetchService::SetHostNonUniqueFilterForTesting( |
| [](base::StringPiece) { return false; }); |
| PrefetchService::SetServiceWorkerContextForTesting( |
| &service_worker_context_); |
| |
| test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>(); |
| attempt_entry_builder_ = |
| std::make_unique<test::PreloadingAttemptUkmEntryBuilder>( |
| content_preloading_predictor::kSpeculationRules); |
| |
| scoped_test_timer_ = |
| std::make_unique<base::ScopedMockElapsedTimersForTest>(); |
| } |
| |
| void TearDown() override { |
| if (PrefetchDocumentManager::GetForCurrentDocument(main_rfh())) |
| PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh()); |
| PrefetchDocumentManager::SetPrefetchServiceForTesting(nullptr); |
| mock_navigation_handle_.reset(); |
| prefetch_service_.reset(); |
| PrefetchService::SetURLLoaderFactoryForTesting(nullptr); |
| PrefetchService::SetHostNonUniqueFilterForTesting(nullptr); |
| PrefetchService::SetServiceWorkerContextForTesting(nullptr); |
| PrefetchService::SetURLLoaderFactoryForTesting(nullptr); |
| test_content_browser_client_.reset(); |
| scoped_feature_list_.Reset(); |
| PreloadingConfig::GetInstance().ParseConfig(); |
| RenderViewHostTestHarness::TearDown(); |
| } |
| |
| virtual void InitScopedFeatureList() { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| |
| void MakePrefetchService(std::unique_ptr<MockPrefetchServiceDelegate> |
| mock_prefetch_service_delegate) { |
| test_content_browser_client_ = |
| std::make_unique<ScopedPrefetchServiceContentBrowserClient>( |
| std::move(mock_prefetch_service_delegate)); |
| |
| prefetch_service_ = PrefetchService::CreateIfPossible(browser_context()); |
| PrefetchDocumentManager::SetPrefetchServiceForTesting( |
| prefetch_service_.get()); |
| } |
| |
| // Creates a prefetch request for |url| on the current main frame. |
| void MakePrefetchOnMainFrame( |
| const GURL& prefetch_url, |
| const PrefetchType& prefetch_type, |
| const blink::mojom::Referrer& referrer = blink::mojom::Referrer(), |
| bool enable_no_vary_search_header = false, |
| network::mojom::NoVarySearchPtr&& no_vary_search_hint = |
| network::mojom::NoVarySearchPtr()) { |
| PrefetchDocumentManager* prefetch_document_manager = |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()); |
| if (enable_no_vary_search_header) |
| prefetch_document_manager->EnableNoVarySearchSupport(); |
| |
| prefetch_document_manager->PrefetchUrl( |
| prefetch_url, prefetch_type, referrer, no_vary_search_hint, |
| blink::mojom::SpeculationInjectionWorld::kNone, nullptr); |
| } |
| |
| int RequestCount() { return test_url_loader_factory_.NumPending(); } |
| |
| void ClearCompletedRequests() { |
| std::vector<network::TestURLLoaderFactory::PendingRequest>* requests = |
| test_url_loader_factory_.pending_requests(); |
| |
| base::EraseIf( |
| *requests, |
| [](const network::TestURLLoaderFactory::PendingRequest& request) { |
| return !request.client.is_connected(); |
| }); |
| } |
| |
| void VerifyCommonRequestState(const GURL& url, bool use_prefetch_proxy) { |
| SCOPED_TRACE(url.spec()); |
| EXPECT_EQ(RequestCount(), 1); |
| |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| |
| EXPECT_EQ(request->request.url, url); |
| EXPECT_EQ(request->request.method, "GET"); |
| EXPECT_TRUE(request->request.enable_load_timing); |
| EXPECT_EQ(request->request.load_flags, |
| net::LOAD_DISABLE_CACHE | net::LOAD_PREFETCH); |
| EXPECT_EQ(request->request.credentials_mode, |
| network::mojom::CredentialsMode::kInclude); |
| |
| std::string purpose_value; |
| EXPECT_TRUE(request->request.headers.GetHeader("Purpose", &purpose_value)); |
| EXPECT_EQ(purpose_value, "prefetch"); |
| |
| std::string sec_purpose_value; |
| EXPECT_TRUE( |
| request->request.headers.GetHeader("Sec-Purpose", &sec_purpose_value)); |
| EXPECT_EQ(sec_purpose_value, |
| use_prefetch_proxy ? "prefetch;anonymous-client-ip" : "prefetch"); |
| |
| std::string accept_value; |
| EXPECT_TRUE(request->request.headers.GetHeader("Accept", &accept_value)); |
| EXPECT_EQ(accept_value, FrameAcceptHeaderValue(/*allow_sxg_responses=*/true, |
| browser_context())); |
| |
| std::string upgrade_insecure_request_value; |
| EXPECT_TRUE(request->request.headers.GetHeader( |
| "Upgrade-Insecure-Requests", &upgrade_insecure_request_value)); |
| EXPECT_EQ(upgrade_insecure_request_value, "1"); |
| |
| ASSERT_TRUE(request->request.trusted_params.has_value()); |
| VerifyIsolationInfo(request->request.trusted_params->isolation_info); |
| } |
| |
| void VerifyIsolationInfo(const net::IsolationInfo& isolation_info) { |
| EXPECT_FALSE(isolation_info.IsEmpty()); |
| EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated()); |
| EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient()); |
| EXPECT_FALSE(isolation_info.site_for_cookies().IsNull()); |
| } |
| |
| network::mojom::URLResponseHeadPtr CreateURLResponseHeadForPrefetch( |
| net::HttpStatusCode http_status, |
| const std::string mime_type, |
| bool use_prefetch_proxy, |
| const std::vector<std::pair<std::string, std::string>>& headers, |
| const GURL& request_url) { |
| auto head = network::CreateURLResponseHead(http_status); |
| |
| head->response_time = base::Time::Now(); |
| head->request_time = |
| head->response_time - base::Milliseconds(kTotalTimeDuration); |
| |
| head->load_timing.connect_timing.connect_end = |
| base::TimeTicks::Now() - base::Minutes(2); |
| head->load_timing.connect_timing.connect_start = |
| head->load_timing.connect_timing.connect_end - |
| base::Milliseconds(kConnectTimeDuration); |
| |
| head->load_timing.receive_headers_end = base::TimeTicks::Now(); |
| head->load_timing.request_start = head->load_timing.receive_headers_end - |
| base::Milliseconds(kHeaderLatency); |
| |
| head->proxy_server = |
| use_prefetch_proxy |
| ? net::ProxyServer::FromSchemeHostAndPort( |
| net::ProxyServer::Scheme::SCHEME_HTTPS, |
| PrefetchProxyHost(GURL(kPrefetchProxyAddress)).spec(), |
| absl::nullopt) |
| : net::ProxyServer::Direct(); |
| |
| head->mime_type = mime_type; |
| for (const auto& header : headers) { |
| head->headers->AddHeader(header.first, header.second); |
| } |
| if (!head->parsed_headers) { |
| head->parsed_headers = |
| network::PopulateParsedHeaders(head->headers.get(), request_url); |
| } |
| |
| return head; |
| } |
| |
| void MakeSingleRedirectAndWait( |
| const net::RedirectInfo& redirect_info, |
| network::mojom::URLResponseHeadPtr redirect_head) { |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| ASSERT_TRUE(request); |
| ASSERT_TRUE(request->client); |
| |
| request->client->OnReceiveRedirect(redirect_info, redirect_head.Clone()); |
| task_environment()->RunUntilIdle(); |
| } |
| |
| void VerifyFollowRedirectParams(size_t expected_follow_redirect_params_size) { |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| ASSERT_TRUE(request); |
| ASSERT_TRUE(request->test_url_loader); |
| |
| const auto& follow_redirect_params = |
| request->test_url_loader->follow_redirect_params(); |
| EXPECT_EQ(follow_redirect_params.size(), |
| expected_follow_redirect_params_size); |
| |
| for (const auto& follow_redirect_param : follow_redirect_params) { |
| EXPECT_EQ(follow_redirect_param.removed_headers.size(), 0U); |
| EXPECT_TRUE(follow_redirect_param.modified_headers.IsEmpty()); |
| EXPECT_TRUE(follow_redirect_param.modified_cors_exempt_headers.IsEmpty()); |
| EXPECT_FALSE(follow_redirect_param.new_url); |
| } |
| } |
| |
| void MakeResponseAndWait( |
| net::HttpStatusCode http_status, |
| net::Error net_error, |
| const std::string mime_type, |
| bool use_prefetch_proxy, |
| std::vector<std::pair<std::string, std::string>> headers, |
| const std::string& body) { |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| ASSERT_TRUE(request); |
| |
| auto head = CreateURLResponseHeadForPrefetch(http_status, mime_type, |
| use_prefetch_proxy, headers, |
| request->request.url); |
| network::URLLoaderCompletionStatus status(net_error); |
| test_url_loader_factory_.AddResponse(request->request.url, std::move(head), |
| body, status); |
| task_environment()->RunUntilIdle(); |
| // Clear responses in the network service so we can inspect the next request |
| // that comes in before it is responded to. |
| test_url_loader_factory_.ClearResponses(); |
| } |
| |
| void SendHeadOfResponseAndWait( |
| net::HttpStatusCode http_status, |
| const std::string mime_type, |
| bool use_prefetch_proxy, |
| std::vector<std::pair<std::string, std::string>> headers, |
| uint32_t expected_total_body_size) { |
| ASSERT_FALSE(producer_handle_); |
| |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| ASSERT_TRUE(request); |
| ASSERT_TRUE(request->client); |
| |
| auto head = CreateURLResponseHeadForPrefetch(http_status, mime_type, |
| use_prefetch_proxy, headers, |
| request->request.url); |
| |
| mojo::ScopedDataPipeConsumerHandle body; |
| EXPECT_EQ( |
| mojo::CreateDataPipe(expected_total_body_size, producer_handle_, body), |
| MOJO_RESULT_OK); |
| |
| request->client->OnReceiveResponse(std::move(head), std::move(body), |
| absl::nullopt); |
| task_environment()->RunUntilIdle(); |
| } |
| |
| void SendBodyContentOfResponseAndWait(const std::string& body) { |
| ASSERT_TRUE(producer_handle_); |
| |
| uint32_t bytes_written = body.size(); |
| EXPECT_EQ(producer_handle_->WriteData(body.data(), &bytes_written, |
| MOJO_WRITE_DATA_FLAG_ALL_OR_NONE), |
| MOJO_RESULT_OK); |
| task_environment()->RunUntilIdle(); |
| } |
| |
| void CompleteResponseAndWait(net::Error net_error, |
| uint32_t expected_total_body_size) { |
| network::TestURLLoaderFactory::PendingRequest* request = |
| test_url_loader_factory_.GetPendingRequest(0); |
| ASSERT_TRUE(request); |
| ASSERT_TRUE(request->client); |
| |
| producer_handle_.reset(); |
| |
| network::URLLoaderCompletionStatus completion_status(net_error); |
| completion_status.decoded_body_length = expected_total_body_size; |
| request->client->OnComplete(completion_status); |
| task_environment()->RunUntilIdle(); |
| |
| test_url_loader_factory_.ClearResponses(); |
| } |
| |
| bool SetCookie(const GURL& url, const std::string& value) { |
| std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create( |
| url, value, base::Time::Now(), /*server_time=*/absl::nullopt, |
| /*cookie_partition_key=*/absl::nullopt)); |
| |
| EXPECT_TRUE(cookie.get()); |
| |
| bool result = false; |
| base::RunLoop run_loop; |
| |
| net::CookieOptions options; |
| options.set_include_httponly(); |
| options.set_same_site_cookie_context( |
| net::CookieOptions::SameSiteCookieContext::MakeInclusive()); |
| |
| cookie_manager_->SetCanonicalCookie( |
| *cookie.get(), url, options, |
| base::BindOnce( |
| [](bool* result, base::RunLoop* run_loop, |
| net::CookieAccessResult set_cookie_access_result) { |
| *result = set_cookie_access_result.status.IsInclude(); |
| run_loop->Quit(); |
| }, |
| &result, &run_loop)); |
| run_loop.Run(); |
| return result; |
| } |
| |
| void Navigate(const GURL& url, |
| const absl::optional<blink::LocalFrameToken>& |
| initiator_local_frame_token) { |
| mock_navigation_handle_ = |
| std::make_unique<testing::NiceMock<MockNavigationHandle>>( |
| web_contents()); |
| mock_navigation_handle_->set_url(url); |
| mock_navigation_handle_->set_initiator_frame_token( |
| base::OptionalToPtr(initiator_local_frame_token)); |
| |
| PrefetchDocumentManager* prefetch_document_manager = |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()); |
| prefetch_document_manager->DidStartNavigation( |
| mock_navigation_handle_.get()); |
| } |
| |
| absl::optional<PrefetchServingPageMetrics> |
| GetMetricsForMostRecentNavigation() { |
| if (!mock_navigation_handle_) |
| return absl::nullopt; |
| |
| return PrefetchServingPageMetrics::GetForNavigationHandle( |
| *mock_navigation_handle_); |
| } |
| |
| base::WeakPtr<PrefetchContainer> GetPrefetchToServe( |
| const GURL& url, |
| GlobalRenderFrameHostId previous_render_frame_host_id = |
| GlobalRenderFrameHostId()) { |
| if (!previous_render_frame_host_id) { |
| // A valid `previous_render_frame_host_id` is given as an argument when |
| // to test that prefetched results are not used for unexpected initiator |
| // Documents. In other cases, use the ID of the expected initiator |
| // Document (RenderFrameHost where the `PrefetchDocumentManager` is |
| // associated). |
| previous_render_frame_host_id = main_rfh()->GetGlobalId(); |
| } |
| base::test::TestFuture<base::WeakPtr<PrefetchContainer>> future; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(previous_render_frame_host_id, url), |
| future.GetCallback()); |
| return future.Get(); |
| } |
| |
| ScopedPrefetchServiceContentBrowserClient* test_content_browser_client() { |
| return test_content_browser_client_.get(); |
| } |
| |
| ukm::TestAutoSetUkmRecorder* test_ukm_recorder() { |
| return test_ukm_recorder_.get(); |
| } |
| |
| const test::PreloadingAttemptUkmEntryBuilder* attempt_entry_builder() { |
| return attempt_entry_builder_.get(); |
| } |
| |
| ukm::SourceId ForceLogsUploadAndGetUkmId() { |
| MockNavigationHandle mock_handle; |
| mock_handle.set_is_in_primary_main_frame(true); |
| mock_handle.set_is_same_document(false); |
| mock_handle.set_has_committed(true); |
| // Makes sure the accurate bit is always false. |
| mock_handle.set_url(GURL("http://Not.Accurate.Trigger.Url/")); |
| auto* preloading_data = |
| PreloadingData::GetOrCreateForWebContents(web_contents()); |
| // Sets the accurate bit, and records `TimeToNextNavigation`. |
| static_cast<PreloadingDataImpl*>(preloading_data) |
| ->DidStartNavigation(&mock_handle); |
| // Records the UKMs. |
| static_cast<PreloadingDataImpl*>(preloading_data) |
| ->DidFinishNavigation(&mock_handle); |
| return mock_handle.GetNextPageUkmSourceId(); |
| } |
| |
| void ExpectCorrectUkmLogs(PreloadingEligibility eligibility, |
| PreloadingHoldbackStatus holdback, |
| PreloadingTriggeringOutcome outcome, |
| PreloadingFailureReason failure, |
| bool is_accurate = false, |
| bool expect_ready_time = false) { |
| const auto source_id = ForceLogsUploadAndGetUkmId(); |
| auto actual_attempts = test_ukm_recorder()->GetEntries( |
| ukm::builders::Preloading_Attempt::kEntryName, |
| test::kPreloadingAttemptUkmMetrics); |
| EXPECT_EQ(actual_attempts.size(), 1u); |
| |
| absl::optional<base::TimeDelta> ready_time = absl::nullopt; |
| if (outcome == PreloadingTriggeringOutcome::kReady || |
| outcome == PreloadingTriggeringOutcome::kSuccess || expect_ready_time) { |
| ready_time = base::ScopedMockElapsedTimersForTest::kMockElapsedTime; |
| } |
| |
| const auto expected_attempts = {attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, eligibility, holdback, outcome, |
| failure, is_accurate, ready_time)}; |
| |
| EXPECT_THAT(actual_attempts, |
| testing::UnorderedElementsAreArray(expected_attempts)) |
| << test::ActualVsExpectedUkmEntriesToString(actual_attempts, |
| expected_attempts); |
| // We do not test the `PreloadingPrediction` as it is added in |
| // `PreloadingDecider`. |
| } |
| |
| protected: |
| FakeServiceWorkerContext service_worker_context_; |
| mojo::Remote<network::mojom::CookieManager> cookie_manager_; |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> |
| test_shared_url_loader_factory_; |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<PrefetchService> prefetch_service_; |
| |
| std::unique_ptr<testing::NiceMock<MockNavigationHandle>> |
| mock_navigation_handle_; |
| |
| std::unique_ptr<ScopedPrefetchServiceContentBrowserClient> |
| test_content_browser_client_; |
| |
| mojo::ScopedDataPipeProducerHandle producer_handle_; |
| std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_; |
| std::unique_ptr<test::PreloadingAttemptUkmEntryBuilder> |
| attempt_entry_builder_; |
| |
| std::unique_ptr<base::ScopedMockElapsedTimersForTest> scoped_test_timer_; |
| }; |
| |
| TEST_F(PrefetchServiceTest, CreateServiceWhenFeatureEnabled) { |
| // Enable feature, which means that we should be able to create a |
| // PrefetchService instance. |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| {features::kPrefetchUseContentRefactor}, |
| {network::features::kPrefetchNoVarySearch}); |
| |
| EXPECT_TRUE(PrefetchService::CreateIfPossible(browser_context())); |
| } |
| |
| TEST_F(PrefetchServiceTest, DontCreateServiceWhenFeatureDisabled) { |
| // Disable feature, which means that we shouldn't be able to create a |
| // PrefetchService instance. |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| {}, {features::kPrefetchUseContentRefactor, |
| network::features::kPrefetchNoVarySearch}); |
| |
| EXPECT_FALSE(PrefetchService::CreateIfPossible(browser_context())); |
| } |
| |
| TEST_F(PrefetchServiceTest, SuccessCase) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| // No servable PrefetchContainer is returned for different RenderFrameHost. |
| GlobalRenderFrameHostId different_render_frame_host_id = |
| main_rfh()->GetGlobalId(); |
| different_render_frame_host_id.child_id += 1; |
| base::WeakPtr<PrefetchContainer> |
| serveable_prefetch_container_for_different_initiator = GetPrefetchToServe( |
| GURL("https://example.com"), different_render_frame_host_id); |
| ASSERT_FALSE(serveable_prefetch_container_for_different_initiator); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| GetPrefetchEagernessHistogramSuffix( |
| blink::mojom::SpeculationEagerness::kEager) |
| .c_str()), |
| false, 1); |
| } |
| |
| TEST_F(PrefetchServiceTest, NoPrefetchingPreloadingDisabled) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/0); |
| |
| // When preloading is disabled, then |PrefetchService| doesn't take the |
| // prefetch at all. |
| EXPECT_CALL(*mock_prefetch_service_delegate, IsSomePreloadingEnabled) |
| .Times(1) |
| .WillOnce(testing::Return(PreloadingEligibility::kPreloadingDisabled)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligiblePreloadingDisabled)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kPreloadingDisabled, |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NoPrefetchingDomainNotInAllowList) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/0); |
| |
| // When referring page is not in allow list, then |PrefetchService| doesn't |
| // take the prefetch at all. |
| EXPECT_CALL(*mock_prefetch_service_delegate, |
| IsDomainInPrefetchAllowList(testing::_)) |
| .Times(1) |
| .WillOnce(testing::Return(false)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_FALSE(serving_page_metrics->prefetch_status); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| // `IsDomainInPrefetchAllowList` returns false so we did not reach the |
| // eligibility check. |
| ExpectCorrectUkmLogs(PreloadingEligibility::kUnspecified, |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceAllowAllDomainsTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"allow_all_domains", "true"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceAllowAllDomainsTest, AllowAllDomains) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| // When "allow_all_domains" is set to true, then we can prefetch from all |
| // domains, not just those in the allow list. |
| EXPECT_CALL(*mock_prefetch_service_delegate, |
| IsDomainInPrefetchAllowList(testing::_)) |
| .Times(0); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceAllowAllDomainsForExtendedPreloadingTest |
| : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"allow_all_domains_for_extended_preloading", "true"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceAllowAllDomainsForExtendedPreloadingTest, |
| ExtendedPreloadingEnabled) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| // Allow all domains if and only if extended preloading is enabled. |
| EXPECT_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled) |
| .Times(1) |
| .WillOnce(testing::Return(true)); |
| EXPECT_CALL(*mock_prefetch_service_delegate, |
| IsDomainInPrefetchAllowList(testing::_)) |
| .Times(0); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceAllowAllDomainsForExtendedPreloadingTest, |
| ExtendedPreloadingDisabled) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/0); |
| |
| // If extended preloading is disabled, then we check the allow list. |
| EXPECT_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled) |
| .Times(1) |
| .WillOnce(testing::Return(false)); |
| EXPECT_CALL(*mock_prefetch_service_delegate, |
| IsDomainInPrefetchAllowList(testing::_)) |
| .Times(1) |
| .WillOnce(testing::Return(false)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_FALSE(serving_page_metrics->prefetch_status); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kUnspecified, |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NonProxiedPrefetchDoesNotRequireAllowList) { |
| base::HistogramTester histogram_tester; |
| |
| // Assume we have a delegate which will not grant access to the proxy for this |
| // domain. Nonetheless a non-proxied prefetch should work. |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| ON_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled) |
| .WillByDefault(testing::Return(false)); |
| ON_CALL(*mock_prefetch_service_delegate, |
| IsDomainInPrefetchAllowList(testing::_)) |
| .WillByDefault(testing::Return(false)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleHostnameNonUnique) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| PrefetchService::SetHostNonUniqueFilterForTesting( |
| [](base::StringPiece) { return true; }); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleHostIsNonUnique)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleHostIsNonUnique), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleDataSaverEnabled) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/0); |
| |
| // When data saver is enabled, then |PrefetchService| doesn't start the |
| // prefetch at all. |
| EXPECT_CALL(*mock_prefetch_service_delegate, IsSomePreloadingEnabled) |
| .Times(1) |
| .WillOnce(testing::Return(PreloadingEligibility::kDataSaverEnabled)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleDataSaverEnabled)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kDataSaverEnabled, |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleNonHttps) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("http://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("http://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleSchemeIsNotHttps)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleSchemeIsNotHttps), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligiblePrefetchProxyNotAvailable) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| // If the prefetch proxy URL is invalid, then we can't make prefetches that |
| // require the proxy. However, non-proxied prefetches are fine. |
| EXPECT_CALL(*mock_prefetch_service_delegate, GetDefaultPrefetchProxyHost) |
| .Times(1) |
| .WillOnce(testing::Return(GURL(""))); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchProxyNotAvailable)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility(PrefetchStatus::kPrefetchProxyNotAvailable), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, |
| EligiblePrefetchProxyNotAvailableNonProxiedPrefetch) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| // If the prefetch proxy URL is invalid, then we can't make prefetches that |
| // require the proxy. However, non-proxied prefetches are fine. |
| EXPECT_CALL(*mock_prefetch_service_delegate, GetDefaultPrefetchProxyHost) |
| .Times(1) |
| .WillOnce(testing::Return(GURL(""))); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleOriginWithinRetryAfterWindow) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| EXPECT_CALL(*mock_prefetch_service_delegate, |
| IsOriginOutsideRetryAfterWindow(GURL("https://example.com"))) |
| .Times(1) |
| .WillOnce(testing::Return(false)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchIneligibleRetryAfter)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility(PrefetchStatus::kPrefetchIneligibleRetryAfter), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, EligibleNonHttpsNonProxiedPotentiallyTrustworthy) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://localhost"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://localhost"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://localhost"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://localhost")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleServiceWorkerRegistered) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| service_worker_context_.AddRegistrationToRegisteredStorageKeys( |
| blink::StorageKey::CreateFromStringForTesting("https://example.com")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>( |
| PrefetchStatus::kPrefetchNotEligibleUserHasServiceWorker)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleUserHasServiceWorker), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, EligibleServiceWorkerNotRegistered) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| service_worker_context_.AddRegistrationToRegisteredStorageKeys( |
| blink::StorageKey::CreateFromStringForTesting("https://other.com")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleUserHasCookies) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleUserHasCookies)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleUserHasCookies), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, EligibleUserHasCookiesForDifferentUrl) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://other.com"), "testing")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, EligibleSameOriginPrefetchCanHaveExistingCookies) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_FailedCookiesChangedAfterPrefetchStarted \ |
| DISABLED_FailedCookiesChangedAfterPrefetchStarted |
| #else |
| #define MAYBE_FailedCookiesChangedAfterPrefetchStarted \ |
| FailedCookiesChangedAfterPrefetchStarted |
| #endif |
| TEST_F(PrefetchServiceTest, MAYBE_FailedCookiesChangedAfterPrefetchStarted) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| // Adding a cookie after the prefetch has started will cause it to fail when |
| // being served. |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| base::RunLoop().RunUntilIdle(); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotUsedCookiesChanged)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| // ReadyTime will be included in the UKM, because the prefetch was ready, and |
| // then failed. |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchNotUsedCookiesChanged), |
| /*is_accurate=*/false, |
| /*expect_ready_time=*/true); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_SameOriginPrefetchIgnoresProxyRequirement \ |
| DISABLED_SameOriginPrefetchIgnoresProxyRequirement |
| #else |
| #define MAYBE_SameOriginPrefetchIgnoresProxyRequirement \ |
| SameOriginPrefetchIgnoresProxyRequirement |
| #endif |
| TEST_F(PrefetchServiceTest, MAYBE_SameOriginPrefetchIgnoresProxyRequirement) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| // Make a same-origin prefetch that requires the proxy. The proxy requirement |
| // is only enforced for cross-origin requests. |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| // serving_page_metrics->required_private_prefetch_proxy will be true if the |
| // prefetch is marked as requiring the proxy when cross origin, even if only |
| // prefetch request was same-origin. |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NotEligibleSameSiteCrossOriginPrefetchRequiresProxy \ |
| DISABLED_NotEligibleSameSiteCrossOriginPrefetchRequiresProxy |
| #else |
| #define MAYBE_NotEligibleSameSiteCrossOriginPrefetchRequiresProxy \ |
| NotEligibleSameSiteCrossOriginPrefetchRequiresProxy |
| #endif |
| TEST_F(PrefetchServiceTest, |
| MAYBE_NotEligibleSameSiteCrossOriginPrefetchRequiresProxy) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| // Make a same-site cross-origin prefetch that requires the proxy. These types |
| // of prefetches are blocked. |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://other.example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://other.example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>( |
| PrefetchStatus:: |
| kPrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://other.example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility( |
| PrefetchStatus:: |
| kPrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotEligibleExistingConnectProxy) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("proxy.com"); |
| TestNetworkContext network_context_for_proxy_lookup(proxy_info); |
| PrefetchService::SetNetworkContextForProxyLookupForTesting( |
| &network_context_for_proxy_lookup); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleExistingProxy)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleExistingProxy), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| |
| PrefetchService::SetNetworkContextForProxyLookupForTesting(nullptr); |
| } |
| |
| TEST_F(PrefetchServiceTest, EligibleExistingConnectProxyButSameOriginPrefetch) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("proxy.com"); |
| TestNetworkContext network_context_for_proxy_lookup(proxy_info); |
| PrefetchService::SetNetworkContextForProxyLookupForTesting( |
| &network_context_for_proxy_lookup); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| |
| PrefetchService::SetNetworkContextForProxyLookupForTesting(nullptr); |
| } |
| |
| TEST_F(PrefetchServiceTest, FailedNon2XXResponseCode) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_NOT_FOUND, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_NOT_FOUND, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedNon2XX)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchFailedNon2XX)); |
| } |
| |
| TEST_F(PrefetchServiceTest, FailedNetError) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", std::abs(net::ERR_FAILED), |
| 1); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedNetError)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchFailedNetError)); |
| } |
| |
| TEST_F(PrefetchServiceTest, HandleRetryAfterResponse) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| EXPECT_CALL( |
| *mock_prefetch_service_delegate, |
| ReportOriginRetryAfter(GURL("https://example.com"), base::Seconds(1234))) |
| .Times(1); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Simulate the origin responding with a "retry-after" header. |
| MakeResponseAndWait(net::HTTP_SERVICE_UNAVAILABLE, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"Retry-After", "1234"}, {"X-Testing", "Hello World"}}, |
| ""); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", |
| net::HTTP_SERVICE_UNAVAILABLE, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedNon2XX)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchFailedNon2XX)); |
| } |
| |
| TEST_F(PrefetchServiceTest, SuccessNonHTML) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| std::string body = "fake PDF"; |
| MakeResponseAndWait(net::HTTP_OK, net::OK, "application/pdf", |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, body); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", body.size(), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NotServeableNavigationInDifferentRenderFrameHost) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| // Since the navigation is occurring in a LocalFrameToken other than where the |
| // prefetch was requested from, we cannot use it. |
| blink::LocalFrameToken other_token(base::UnguessableToken::Create()); |
| ASSERT_NE(other_token, main_rfh()->GetFrameToken()); |
| Navigate(GURL("https://example.com"), other_token); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| EXPECT_FALSE(serving_page_metrics); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceLimitedPrefetchesTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"max_srp_prefetches", "2"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceLimitedPrefetchesTest, LimitedNumberOfPrefetches) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/3)); |
| |
| // Make 3 prefetches from the same page. PrefetchService should make requests |
| // for the first two prefetches but not the third due to the limit on the |
| // number of prefetches. |
| MakePrefetchOnMainFrame( |
| GURL("https://example1.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example1.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example2.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example2.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example3.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 3); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 2); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 2); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 2); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 2); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 2); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 3); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 3); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 2); |
| |
| Navigate(GURL("https://example1.com"), main_rfh()->GetFrameToken()); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics1 = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics1); |
| EXPECT_TRUE(serving_page_metrics1->prefetch_status); |
| EXPECT_EQ(serving_page_metrics1->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics1->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics1->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics1->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics1->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container1 = |
| GetPrefetchToServe(GURL("https://example1.com")); |
| ASSERT_TRUE(serveable_prefetch_container1); |
| EXPECT_TRUE(serveable_prefetch_container1->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container1->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE(serveable_prefetch_container1->IsPrefetchServable( |
| base::TimeDelta::Max())); |
| |
| Navigate(GURL("https://example2.com"), main_rfh()->GetFrameToken()); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics2 = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics2); |
| EXPECT_TRUE(serving_page_metrics2->prefetch_status); |
| EXPECT_EQ(serving_page_metrics2->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics2->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics2->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics2->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics2->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container2 = |
| GetPrefetchToServe(GURL("https://example2.com")); |
| ASSERT_TRUE(serveable_prefetch_container2); |
| EXPECT_TRUE(serveable_prefetch_container2->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container2->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE(serveable_prefetch_container2->IsPrefetchServable( |
| base::TimeDelta::Max())); |
| |
| Navigate(GURL("https://example3.com"), main_rfh()->GetFrameToken()); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics3 = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics3); |
| // The prefetch attempt that exceeds the limit is just rejected with no |
| // chance to update PrefetchServingPageMetrics. |
| EXPECT_FALSE(serving_page_metrics3->prefetch_status); |
| EXPECT_FALSE(serving_page_metrics3->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics3->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics3->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container3 = |
| GetPrefetchToServe(GURL("https://example3.com")); |
| EXPECT_FALSE(serveable_prefetch_container3); |
| { |
| const auto source_id = ForceLogsUploadAndGetUkmId(); |
| auto actual_attempts = test_ukm_recorder()->GetEntries( |
| ukm::builders::Preloading_Attempt::kEntryName, |
| test::kPreloadingAttemptUkmMetrics); |
| EXPECT_EQ(actual_attempts.size(), 3u); |
| |
| // The third entry never reaches the holdback status check. |
| std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts = |
| {attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime), |
| attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime), |
| attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| content::PrefetchStatus::kPrefetchFailedPerPageLimitExceeded), |
| /*accurate=*/false)}; |
| EXPECT_THAT(actual_attempts, |
| testing::UnorderedElementsAreArray(expected_attempts)) |
| << test::ActualVsExpectedUkmEntriesToString(actual_attempts, |
| expected_attempts); |
| } |
| } |
| |
| class PrefetchServiceWithHTMLOnlyTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"html_only", "true"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceWithHTMLOnlyTest, FailedNonHTMLWithHTMLOnly) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| std::string body = "fake PDF"; |
| MakeResponseAndWait(net::HTTP_OK, net::OK, "application/pdf", |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, body); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", body.size(), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedMIMENotSupported)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedMIMENotSupported)); |
| } |
| |
| class PrefetchServiceAlwaysMakeDecoyRequestTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "1"}, |
| {"prefetch_container_lifetime_s", "-1"}}}, |
| {features::kPrefetchRedirects, {}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceAlwaysMakeDecoyRequestTest, DecoyRequest) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchIsPrivacyDecoy)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| // A decoy is considered a failure. |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchIsPrivacyDecoy)); |
| } |
| |
| TEST_F(PrefetchServiceAlwaysMakeDecoyRequestTest, |
| NoDecoyRequestDisableDecoysBasedOnUserSettings) { |
| base::HistogramTester histogram_tester; |
| |
| std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate = |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(); |
| |
| EXPECT_CALL(*mock_prefetch_service_delegate, DisableDecoysBasedOnUserSettings) |
| .Times(1) |
| .WillOnce(testing::Return(true)); |
| |
| MakePrefetchService(std::move(mock_prefetch_service_delegate)); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotEligibleUserHasCookies)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleUserHasCookies), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectDecoyRequest DISABLED_RedirectDecoyRequest |
| #else |
| #define MAYBE_RedirectDecoyRequest RedirectDecoyRequest |
| #endif |
| TEST_F(PrefetchServiceAlwaysMakeDecoyRequestTest, MAYBE_RedirectDecoyRequest) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| service_worker_context_.AddRegistrationToRegisteredStorageKeys( |
| blink::StorageKey::CreateFromStringForTesting("https://redirect.com")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| |
| // The redirect is ineligible, but will be followed since the prefetch is now |
| // a decoy. |
| VerifyFollowRedirectParams(1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchIsPrivacyDecoy)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchIsPrivacyDecoy)); |
| } |
| |
| class PrefetchServiceHoldbackTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"prefetch_holdback", "true"}}}}, |
| {::features::kPreloadingConfig}); |
| } |
| }; |
| |
| TEST_F(PrefetchServiceHoldbackTest, PrefetchHeldback) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| // Holdback is checked and set after eligibility. |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchHeldback)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kHoldback, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceIncognitoTest : public PrefetchServiceTest { |
| protected: |
| std::unique_ptr<BrowserContext> CreateBrowserContext() override { |
| auto browser_context = std::make_unique<TestBrowserContext>(); |
| browser_context->set_is_off_the_record(true); |
| return browser_context; |
| } |
| }; |
| |
| TEST_F(PrefetchServiceIncognitoTest, OffTheRecordIneligible) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>( |
| PrefetchStatus::kPrefetchNotEligibleBrowserContextOffTheRecord)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleBrowserContextOffTheRecord), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| TEST_F(PrefetchServiceTest, NonDefaultStoragePartition) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| test_content_browser_client_->UseOffTheRecordContextForStoragePartition(true); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(RequestCount(), 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 0); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>( |
| PrefetchStatus::kPrefetchNotEligibleNonDefaultStoragePartition)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs( |
| ToPreloadingEligibility( |
| PrefetchStatus::kPrefetchNotEligibleNonDefaultStoragePartition), |
| PreloadingHoldbackStatus::kUnspecified, |
| PreloadingTriggeringOutcome::kUnspecified, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceStreamingURLLoaderTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"use_streaming_url_loader", "true"}}}}, |
| {::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_StreamingURLLoaderSuccessCase \ |
| DISABLED_StreamingURLLoaderSuccessCase |
| #else |
| #define MAYBE_StreamingURLLoaderSuccessCase StreamingURLLoaderSuccessCase |
| #endif |
| TEST_F(PrefetchServiceStreamingURLLoaderTest, |
| MAYBE_StreamingURLLoaderSuccessCase) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Send the head of the navigation. The prefetch should be servable after this |
| // point. The body of the response will be streaming to the serving URL loader |
| // as its received. |
| SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, |
| std::size(kHTMLBody)); |
| |
| // Navigate to the URL before the prefetch response is complete. |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Check the metrics while the prefetch is still in progress. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotFinishedInTime)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchNotFinishedInTime); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| // Send the body and completion status of the request, then recheck all of the |
| // metrics. |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| serving_page_metrics = GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceNoVarySearchTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeatures( |
| {features::kPrefetchUseContentRefactor, |
| network::features::kPrefetchNoVarySearch}, |
| {::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NoVarySearchSuccessCase DISABLED_NoVarySearchSuccessCase |
| #else |
| #define MAYBE_NoVarySearchSuccessCase NoVarySearchSuccessCase |
| #endif |
| TEST_F(PrefetchServiceNoVarySearchTest, MAYBE_NoVarySearchSuccessCase) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com/?a=1"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/blink::mojom::Referrer(), |
| /*enable_no_vary_search_header=*/true); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com/?a=1"), |
| /*use_prefetch_proxy=*/true); |
| MakeResponseAndWait( |
| net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}, {"No-Vary-Search", R"(params=("a"))"}}, |
| kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_EQ(serveable_prefetch_container->GetURL(), |
| GURL("https://example.com/?a=1")); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceAllowRedirectTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}}}, |
| {features::kPrefetchRedirects, {}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_PrefetchEligibleRedirect DISABLED_PrefetchEligibleRedirect |
| #else |
| #define MAYBE_PrefetchEligibleRedirect PrefetchEligibleRedirect |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, MAYBE_PrefetchEligibleRedirect) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| VerifyFollowRedirectParams(1); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_IneligibleRedirectCookies DISABLED_IneligibleRedirectCookies |
| #else |
| #define MAYBE_IneligibleRedirectCookies IneligibleRedirectCookies |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, MAYBE_IneligibleRedirectCookies) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| ASSERT_TRUE(SetCookie(GURL("https://redirect.com"), "testing")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| |
| // Since the redirect URL has cookies, it is ineligible for prefetching and |
| // causes the prefetch to fail. Also since checking if the URL has cookies |
| // requires mojo, the eligibility check will not complete immediately. |
| VerifyFollowRedirectParams(0); |
| |
| histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kFailedIneligible, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 0); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_IneligibleRedirectServiceWorker \ |
| DISABLED_IneligibleRedirectServiceWorker |
| #else |
| #define MAYBE_IneligibleRedirectServiceWorker IneligibleRedirectServiceWorker |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_IneligibleRedirectServiceWorker) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| service_worker_context_.AddRegistrationToRegisteredStorageKeys( |
| blink::StorageKey::CreateFromStringForTesting("https://redirect.com")); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| |
| // Since the redirect URL has cookies, it is ineligible for prefetching and |
| // causes the prefetch to fail. Also the eligibility check should fail |
| // immediately. |
| VerifyFollowRedirectParams(0); |
| |
| histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kFailedIneligible, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 0); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_InvalidRedirect DISABLED_InvalidRedirect |
| #else |
| #define MAYBE_InvalidRedirect InvalidRedirect |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, MAYBE_InvalidRedirect) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| // The redirect is considered invalid because it has a non-3XX HTTP code. |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait(redirect_info, CreateURLResponseHeadForPrefetch( |
| net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, |
| GURL("https://redirect.com"))); |
| VerifyFollowRedirectParams(0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kFailedInvalidResponseCode, 1); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedInvalidRedirect)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 0); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedInvalidRedirect)); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_PrefetchSameOriginEligibleRedirect \ |
| DISABLED_PrefetchSameOriginEligibleRedirect |
| #else |
| #define MAYBE_PrefetchSameOriginEligibleRedirect \ |
| PrefetchSameOriginEligibleRedirect |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_PrefetchSameOriginEligibleRedirect) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://example.com/redirect"); |
| MakeSingleRedirectAndWait(redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, |
| GURL("https://example.com/redirect"))); |
| VerifyFollowRedirectParams(1); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kDefaultToDefault, 1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| // TODO(https://crbug.com/1439986): This test is testing the current |
| // functionality, and should be removed while fixing this bug. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_IneligibleSameSiteCrossOriginRequiresProxyRedirect \ |
| DISABLED_IneligibleSameSiteCrossOriginRequiresProxyRedirect |
| #else |
| #define MAYBE_IneligibleSameSiteCrossOriginRequiresProxyRedirect \ |
| IneligibleSameSiteCrossOriginRequiresProxyRedirect |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_IneligibleSameSiteCrossOriginRequiresProxyRedirect) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // The request to the same-origin prefetch URL should ignore the proxy |
| // requirement, since it only applies to cross-origin prefetches. |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| // Redirect to a same-site cross-origin URL. The proxy requirement should |
| // apply to this URL, and result in the redirect being marked as ineligible, |
| // because we cannot make same-site cross-origin requests that require the |
| // proxy. |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://other.example.com/redirect"); |
| MakeSingleRedirectAndWait(redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, |
| GURL("https://example.com/redirect"))); |
| VerifyFollowRedirectParams(0); |
| |
| histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kFailedIneligible, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kDefaultToDefault, 1); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ( |
| serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 0); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedIneligibleRedirect)); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectDefaultToIsolatedNetworkContextTransition \ |
| DISABLED_RedirectDefaultToIsolatedNetworkContextTransition |
| #else |
| #define MAYBE_RedirectDefaultToIsolatedNetworkContextTransition \ |
| RedirectDefaultToIsolatedNetworkContextTransition |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_RedirectDefaultToIsolatedNetworkContextTransition) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Since the redirect is cross-site compared to the referrer. A new request |
| // will be started in an isolated network context, and the redirect will not |
| // be followed directly. |
| VerifyFollowRedirectParams(0); |
| ClearCompletedRequests(); |
| VerifyCommonRequestState(GURL("https://redirect.com"), |
| /*use_prefetch_proxy=*/false); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectDefaultToIsolatedNetworkContextTransitionWithProxy \ |
| DISABLED_RedirectDefaultToIsolatedNetworkContextTransitionWithProxy |
| #else |
| #define MAYBE_RedirectDefaultToIsolatedNetworkContextTransitionWithProxy \ |
| RedirectDefaultToIsolatedNetworkContextTransitionWithProxy |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_RedirectDefaultToIsolatedNetworkContextTransitionWithProxy) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The same-origin request should not use the proxy. |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Since the redirect is cross-site compared to the referrer. A new request |
| // will be started in an isolated network context, and the redirect will not |
| // be followed directly. The new request should use the proxy. |
| VerifyFollowRedirectParams(0); |
| ClearCompletedRequests(); |
| VerifyCommonRequestState(GURL("https://redirect.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectIsolatedToDefaultNetworkContextTransition \ |
| DISABLED_RedirectIsolatedToDefaultNetworkContextTransition |
| #else |
| #define MAYBE_RedirectIsolatedToDefaultNetworkContextTransition \ |
| RedirectIsolatedToDefaultNetworkContextTransition |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_RedirectIsolatedToDefaultNetworkContextTransition) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://other.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://other.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://example.com/redirect"); |
| MakeSingleRedirectAndWait(redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, |
| GURL("https://example.com/redirect"))); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Since the redirect is same-site compared to the referrer. A new request |
| // will be started in the default network context, and the redirect will not |
| // be followed directly. |
| VerifyFollowRedirectParams(0); |
| ClearCompletedRequests(); |
| VerifyCommonRequestState(GURL("https://example.com/redirect"), |
| /*use_prefetch_proxy=*/false); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kIsolatedToDefault, 1); |
| |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| Navigate(GURL("https://other.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://other.com")); |
| ASSERT_TRUE(serveable_prefetch_container); |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceAllowRedirectsAndAlwaysBlockUntilHeadTest |
| : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"block_until_head_eager_prefetch", "true"}, |
| {"block_until_head_moderate_prefetch", "true"}, |
| {"block_until_head_conservative_prefetch", "true"}}}, |
| {features::kPrefetchRedirects, {}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectNetworkContextTransitionBlockUntilHead \ |
| DISABLED_RedirectNetworkContextTransitionBlockUntilHead |
| #else |
| #define MAYBE_RedirectNetworkContextTransitionBlockUntilHead \ |
| RedirectNetworkContextTransitionBlockUntilHead |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectsAndAlwaysBlockUntilHeadTest, |
| MAYBE_RedirectNetworkContextTransitionBlockUntilHead) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://example.com/referrer"); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/false); |
| VerifyFollowRedirectParams(0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| VLOG(0) << "prefetch_to_serve == nullptr = " |
| << (prefetch_to_serve == nullptr ? "true" : "false"); |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Since the redirect is cross-site compared to the referrer. A new request |
| // will be started in an isolated network context, and the redirect will not |
| // be followed directly. |
| VerifyFollowRedirectParams(0); |
| ClearCompletedRequests(); |
| VerifyCommonRequestState(GURL("https://redirect.com"), |
| /*use_prefetch_proxy=*/false); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kSuccessRedirectFollowed, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", |
| PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1); |
| |
| // Once the final response to the prefetch is received, then callback given to |
| // |GetPrefetchToServe| should be run. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| get_prefetch_run_loop.Run(); |
| ASSERT_TRUE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_FALSE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_RedirectInsufficientReferrerPolicy \ |
| DISABLED_RedirectInsufficientReferrerPolicy |
| #else |
| #define MAYBE_RedirectInsufficientReferrerPolicy \ |
| RedirectInsufficientReferrerPolicy |
| #endif |
| TEST_F(PrefetchServiceAllowRedirectTest, |
| MAYBE_RedirectInsufficientReferrerPolicy) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| blink::mojom::Referrer referrer; |
| referrer.url = GURL("https://referrer.com"); |
| referrer.policy = network::mojom::ReferrerPolicy::kDefault; |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager), |
| /*referrer=*/referrer); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| VerifyFollowRedirectParams(0); |
| |
| // Redirect to a different site. This will check the referrer policy, but |
| // since it is not sufficiently strict, the redirect should fail. |
| net::RedirectInfo redirect_info; |
| redirect_info.new_method = "GET"; |
| redirect_info.new_referrer_policy = net::ReferrerPolicy::NEVER_CLEAR; |
| redirect_info.new_url = GURL("https://redirect.com"); |
| MakeSingleRedirectAndWait( |
| redirect_info, |
| CreateURLResponseHeadForPrefetch( |
| net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"))); |
| VerifyFollowRedirectParams(0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Redirect.Result", |
| PrefetchRedirectResult::kFailedInsufficientReferrerPolicy, 1); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Redirect.NetworkContextStateTransition", 0); |
| |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedInvalidRedirect)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.AfterClick.RedirectChainSize", 0); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchFailedInvalidRedirect)); |
| } |
| |
| class PrefetchServiceNeverBlockUntilHeadTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"block_until_head_eager_prefetch", "false"}, |
| {"block_until_head_moderate_prefetch", "false"}, |
| {"block_until_head_conservative_prefetch", "false"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_HeadNotReceived DISABLED_HeadNotReceived |
| #else |
| #define MAYBE_HeadNotReceived HeadNotReceived |
| #endif |
| TEST_F(PrefetchServiceNeverBlockUntilHeadTest, MAYBE_HeadNotReceived) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received. |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Since PrefetchService cannot block until headers for this prefetch, it |
| // should immediately return null. |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(GURL("https://example.com")); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError", |
| 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotFinishedInTime)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kRunning, |
| PreloadingFailureReason::kUnspecified); |
| } |
| |
| class PrefetchServiceAlwaysBlockUntilHeadTest |
| : public PrefetchServiceTest, |
| public ::testing::WithParamInterface<blink::mojom::SpeculationEagerness> { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| { |
| {"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"prefetch_timeout_ms", "10000"}, |
| {"block_until_head_eager_prefetch", "true"}, |
| {"block_until_head_moderate_prefetch", "true"}, |
| {"block_until_head_conservative_prefetch", "true"}, |
| {"block_until_head_timeout_eager_prefetch", "0"}, |
| {"block_until_head_timeout_moderate_prefetch", "0"}, |
| {"block_until_head_timeout_conservative_prefetch", "0"}, |
| }}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_BlockUntilHeadReceived DISABLED_BlockUntilHeadReceived |
| #else |
| #define MAYBE_BlockUntilHeadReceived BlockUntilHeadReceived |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, MAYBE_BlockUntilHeadReceived) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(500)); |
| |
| // Sends the head of the prefetch response. This should trigger the above |
| // callback. |
| SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, |
| std::size(kHTMLBody)); |
| get_prefetch_run_loop.Run(); |
| ASSERT_TRUE(serveable_prefetch_container); |
| |
| // Send the body and completion status of the request, |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.Served.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(500), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NVSBlockUntilHeadReceived DISABLED_NVSBlockUntilHeadReceived |
| #else |
| #define MAYBE_NVSBlockUntilHeadReceived NVSBlockUntilHeadReceived |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_NVSBlockUntilHeadReceived) { |
| // For this test we need to enable kPrefetchNoVarySearch. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({network::features::kPrefetchNoVarySearch}, {}); |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| network::mojom::NoVarySearchPtr no_vary_search_hint = |
| network::mojom::NoVarySearch::New(); |
| no_vary_search_hint->vary_on_key_order = true; |
| no_vary_search_hint->search_variance = |
| network::mojom::SearchParamsVariance::NewNoVaryParams( |
| std::vector<std::string>({"a"})); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com/index.html?a=5"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam()), |
| /* referrer */ blink::mojom::Referrer(), |
| /* no_vary_search_support */ true, |
| /* no_vary_search_hint */ std::move(no_vary_search_hint)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com/index.html?a=5"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com/index.html"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com/index.html")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(600)); |
| |
| // Sends the head of the prefetch response. This should trigger the above |
| // callback. |
| SendHeadOfResponseAndWait( |
| net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"a\")"}}, |
| std::size(kHTMLBody)); |
| get_prefetch_run_loop.Run(); |
| ASSERT_TRUE(serveable_prefetch_container); |
| |
| // Send the body and completion status of the request, |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| EXPECT_TRUE(serveable_prefetch_container->HasPrefetchStatus()); |
| EXPECT_EQ(serveable_prefetch_container->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| EXPECT_TRUE( |
| serveable_prefetch_container->IsPrefetchServable(base::TimeDelta::Max())); |
| ASSERT_TRUE(serveable_prefetch_container->GetHead()); |
| EXPECT_TRUE(serveable_prefetch_container->GetHead()->was_in_prefetch_cache); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate*/ true); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.Served.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(600), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NVSBlockUntilHeadReceivedNoMatchNoNVSHeader \ |
| DISABLED_NVSBlockUntilHeadReceivedNoMatchNoNVSHeader |
| #else |
| #define MAYBE_NVSBlockUntilHeadReceivedNoMatchNoNVSHeader \ |
| NVSBlockUntilHeadReceivedNoMatchNoMatchNoNVSHeader |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_NVSBlockUntilHeadReceivedNoMatchNoNVSHeader) { |
| // For this test we need to enable kPrefetchNoVarySearch. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({network::features::kPrefetchNoVarySearch}, {}); |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| network::mojom::NoVarySearchPtr no_vary_search_hint = |
| network::mojom::NoVarySearch::New(); |
| no_vary_search_hint->vary_on_key_order = true; |
| no_vary_search_hint->search_variance = |
| network::mojom::SearchParamsVariance::NewNoVaryParams( |
| std::vector<std::string>({"a"})); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com/index.html?a=5"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam()), |
| /* referrer */ blink::mojom::Referrer(), |
| /* no_vary_search_support */ true, |
| /* no_vary_search_hint */ std::move(no_vary_search_hint)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com/index.html?a=5"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com/index.html"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| bool is_nav_unblocked = false; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com/index.html")), |
| base::BindOnce( |
| [](bool* is_nav_unblocked, base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *is_nav_unblocked = !prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &is_nav_unblocked, &get_prefetch_run_loop)); |
| EXPECT_FALSE(is_nav_unblocked); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(700)); |
| |
| // Sends the head of the prefetch response. This should trigger the above |
| // callback with a nullptr argument. |
| SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, |
| std::size(kHTMLBody)); |
| get_prefetch_run_loop.Run(); |
| ASSERT_TRUE(is_nav_unblocked); |
| |
| // Send the body and completion status of the request, |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate*/ false); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(700), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NVSBlockUntilHeadReceivedNoMatchByNVSHeader \ |
| DISABLED_NVSBlockUntilHeadReceivedNoMatchByNVSHeader |
| #else |
| #define MAYBE_NVSBlockUntilHeadReceivedNoMatchByNVSHeader \ |
| NVSBlockUntilHeadReceivedNoMatchNoMatchByNVSHeader |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_NVSBlockUntilHeadReceivedNoMatchByNVSHeader) { |
| // For this test we need to enable kPrefetchNoVarySearch. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({network::features::kPrefetchNoVarySearch}, {}); |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| network::mojom::NoVarySearchPtr no_vary_search_hint = |
| network::mojom::NoVarySearch::New(); |
| no_vary_search_hint->vary_on_key_order = true; |
| no_vary_search_hint->search_variance = |
| network::mojom::SearchParamsVariance::NewNoVaryParams( |
| std::vector<std::string>({"a"})); |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com/index.html?a=5"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam()), |
| /* referrer */ blink::mojom::Referrer(), |
| /* no_vary_search_support */ true, |
| /* no_vary_search_hint */ std::move(no_vary_search_hint)); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com/index.html?a=5"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com/index.html"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| bool is_nav_unblocked = false; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com/index.html")), |
| base::BindOnce( |
| [](bool* is_nav_unblocked, base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *is_nav_unblocked = !prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &is_nav_unblocked, &get_prefetch_run_loop)); |
| EXPECT_FALSE(is_nav_unblocked); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(400)); |
| |
| // Sends the head of the prefetch response. This should trigger the above |
| // callback with a nullptr argument. |
| SendHeadOfResponseAndWait( |
| net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"b\")"}}, |
| std::size(kHTMLBody)); |
| get_prefetch_run_loop.Run(); |
| ASSERT_TRUE(is_nav_unblocked); |
| |
| // Send the body and completion status of the request, |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate*/ false); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(400), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_FailedCookiesChangedWhileBlockUntilHead \ |
| DISABLED_FailedCookiesChangedWhileBlockUntilHead |
| #else |
| #define MAYBE_FailedCookiesChangedWhileBlockUntilHead \ |
| FailedCookiesChangedWhileBlockUntilHead |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_FailedCookiesChangedWhileBlockUntilHead) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(800)); |
| |
| // Adding a cookie after while blocking until the head is received will cause |
| // it to fail. |
| ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing")); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Sends the head of the prefetch response. This should trigger the above |
| // callback. |
| SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, |
| std::size(kHTMLBody)); |
| get_prefetch_run_loop.Run(); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| // Send the body and completion status of the request, |
| SendBodyContentOfResponseAndWait(kHTMLBody); |
| CompleteResponseAndWait(net::OK, std::size(kHTMLBody)); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchNotUsedCookiesChanged)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| PrefetchStatus::kPrefetchNotUsedCookiesChanged)); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(800), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_FailedTimeoutWhileBlockUntilHead \ |
| DISABLED_FailedTimeoutWhileBlockUntilHead |
| #else |
| #define MAYBE_FailedTimeoutWhileBlockUntilHead FailedTimeoutWhileBlockUntilHead |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_FailedTimeoutWhileBlockUntilHead) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| // If the prefetch times out while PrefetchService is blocking until head, |
| // then it should unblock without setting serveable_prefetch_container. |
| task_environment()->FastForwardBy(base::Milliseconds(10000)); |
| get_prefetch_run_loop.Run(); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", std::abs(net::ERR_TIMED_OUT), |
| 1); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedNetError)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchFailedNetError)); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(10000), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| // TODO(crbug.com/1396460): Test flaky on lacros trybots. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_FailedNetErrorWhileBlockUntilHead \ |
| DISABLED_FailedNetErrorWhileBlockUntilHead |
| #else |
| #define MAYBE_FailedNetErrorWhileBlockUntilHead \ |
| FailedNetErrorWhileBlockUntilHead |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest, |
| MAYBE_FailedNetErrorWhileBlockUntilHead) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback shouldn't |
| // be called until after the head is received. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(300)); |
| |
| // If the prefetch encounters a net error while PrefetchService is blocking |
| // until head, then it should unblock without setting |
| // serveable_prefetch_container. |
| CompleteResponseAndWait(net::ERR_ACCESS_DENIED, 0); |
| get_prefetch_run_loop.Run(); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode", |
| 0); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", |
| std::abs(net::ERR_ACCESS_DENIED), 1); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", 0); |
| histogram_tester.ExpectTotalCount( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchFailedNetError)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_FALSE(serving_page_metrics->prefetch_header_latency); |
| |
| ExpectCorrectUkmLogs( |
| PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason(PrefetchStatus::kPrefetchFailedNetError)); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(300), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ParametrizedTests, |
| PrefetchServiceAlwaysBlockUntilHeadTest, |
| testing::Values(blink::mojom::SpeculationEagerness::kModerate, |
| blink::mojom::SpeculationEagerness::kConservative)); |
| |
| class PrefetchServiceAlwaysBlockUntilHeadWithTimeoutTest |
| : public PrefetchServiceTest, |
| public ::testing::WithParamInterface<blink::mojom::SpeculationEagerness> { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| { |
| {"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}, |
| {"prefetch_timeout_ms", "10000"}, |
| {"block_until_head_eager_prefetch", "true"}, |
| {"block_until_head_moderate_prefetch", "true"}, |
| {"block_until_head_conservative_prefetch", "true"}, |
| {"block_until_head_timeout_eager_prefetch", "1000"}, |
| {"block_until_head_timeout_moderate_prefetch", "1000"}, |
| {"block_until_head_timeout_conservative_prefetch", "1000"}, |
| }}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_BlockUntilHeadTimedout DISABLED_BlockUntilHeadTimedout |
| #else |
| #define MAYBE_BlockUntilHeadTimedout BlockUntilHeadTimedout |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadWithTimeoutTest, |
| MAYBE_BlockUntilHeadTimedout) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback should be |
| // triggered once the timeout is exceeded. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(1000)); |
| get_prefetch_run_loop.Run(); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| // If the prefetch is received after the block until head has timed out, it |
| // will not be used. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(1000), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_HeadReceivedBeforeTimeout DISABLED_HeadReceivedBeforeTimeout |
| #else |
| #define MAYBE_HeadReceivedBeforeTimeout HeadReceivedBeforeTimeout |
| #endif |
| TEST_P(PrefetchServiceAlwaysBlockUntilHeadWithTimeoutTest, |
| MAYBE_HeadReceivedBeforeTimeout) { |
| base::HistogramTester histogram_tester; |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>()); |
| |
| MakePrefetchOnMainFrame( |
| GURL("https://example.com"), |
| PrefetchType(/*use_prefetch_proxy=*/true, GetParam())); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyCommonRequestState(GURL("https://example.com"), |
| /*use_prefetch_proxy=*/true); |
| |
| // Navigate to the URL before the head of the prefetch response is received |
| Navigate(GURL("https://example.com"), main_rfh()->GetFrameToken()); |
| |
| // Request the prefetch from the PrefetchService. The given callback should be |
| // triggered once the timeout is exceeded. |
| base::RunLoop get_prefetch_run_loop; |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container; |
| prefetch_service_->GetPrefetchToServe( |
| PrefetchContainer::Key(main_rfh()->GetGlobalId(), |
| GURL("https://example.com")), |
| base::BindOnce( |
| [](base::WeakPtr<PrefetchContainer>* serveable_prefetch_container, |
| base::RunLoop* get_prefetch_run_loop, |
| base::WeakPtr<PrefetchContainer> prefetch_to_serve) { |
| *serveable_prefetch_container = prefetch_to_serve; |
| get_prefetch_run_loop->Quit(); |
| }, |
| &serveable_prefetch_container, &get_prefetch_run_loop)); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| task_environment()->FastForwardBy(base::Milliseconds(1000)); |
| get_prefetch_run_loop.Run(); |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| // If the prefetch is received after the block until head has timed out, it |
| // will not be used. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/true, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| |
| // Check the metrics now that the prefetch is complete. |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1); |
| histogram_tester.ExpectUniqueSample( |
| "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1); |
| |
| absl::optional<PrefetchServingPageMetrics> serving_page_metrics = |
| GetMetricsForMostRecentNavigation(); |
| ASSERT_TRUE(serving_page_metrics); |
| EXPECT_TRUE(serving_page_metrics->prefetch_status); |
| EXPECT_EQ(serving_page_metrics->prefetch_status.value(), |
| static_cast<int>(PrefetchStatus::kPrefetchSuccessful)); |
| EXPECT_TRUE(serving_page_metrics->required_private_prefetch_proxy); |
| EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab); |
| EXPECT_TRUE(serving_page_metrics->prefetch_header_latency); |
| EXPECT_EQ(serving_page_metrics->prefetch_header_latency.value(), |
| base::Milliseconds(kHeaderLatency)); |
| |
| EXPECT_FALSE(serveable_prefetch_container); |
| |
| ExpectCorrectUkmLogs(PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified); |
| |
| std::string histogram_suffix = |
| GetPrefetchEagernessHistogramSuffix(GetParam()); |
| histogram_tester.ExpectUniqueTimeSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.BlockUntilHeadDuration.NotServed.%s", |
| histogram_suffix.c_str()), |
| base::Milliseconds(1000), 1); |
| histogram_tester.ExpectUniqueSample( |
| base::StringPrintf( |
| "PrefetchProxy.AfterClick.WasBlockedUntilHeadWhenServing.%s", |
| histogram_suffix.c_str()), |
| true, 1); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ParametrizedTests, |
| PrefetchServiceAlwaysBlockUntilHeadWithTimeoutTest, |
| testing::Values(blink::mojom::SpeculationEagerness::kModerate, |
| blink::mojom::SpeculationEagerness::kConservative)); |
| |
| class PrefetchServiceNewLimitsTest : public PrefetchServiceTest { |
| public: |
| void InitScopedFeatureList() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kPrefetchUseContentRefactor, |
| {{"ineligible_decoy_request_probability", "0"}, |
| {"prefetch_container_lifetime_s", "-1"}}}, |
| {features::kPrefetchNewLimits, |
| {{"max_eager_prefetches", "2"}, {"max_non_eager_prefetches", "2"}}}}, |
| {network::features::kPrefetchNoVarySearch, |
| ::features::kPreloadingConfig}); |
| } |
| |
| base::WeakPtr<PrefetchContainer> CompletePrefetch( |
| GURL url, |
| blink::mojom::SpeculationEagerness eagerness) { |
| MakePrefetchOnMainFrame( |
| url, PrefetchType(/*use_prefetch_proxy=*/false, eagerness)); |
| base::RunLoop().RunUntilIdle(); |
| return CompleteExistingPrefetch(url); |
| } |
| |
| // Unlike the above method, this expects the prefetch for |url| to have |
| // already been triggered. |
| base::WeakPtr<PrefetchContainer> CompleteExistingPrefetch(GURL url) { |
| VerifyCommonRequestState(url, |
| /*use_prefetch_proxy=*/false); |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| Navigate(url, main_rfh()->GetFrameToken()); |
| base::WeakPtr<PrefetchContainer> servable_prefetch_container = |
| GetPrefetchToServe(url); |
| return servable_prefetch_container; |
| } |
| }; |
| |
| TEST_F(PrefetchServiceNewLimitsTest, |
| NonEagerPrefetchAllowedWhenEagerLimitIsReached) { |
| const GURL url_1 = GURL("https://example.com/one"); |
| const GURL url_2 = GURL("https://example.com/two"); |
| const GURL url_3 = GURL("https://example.com/three"); |
| const GURL url_4 = GURL("https://example.com/four"); |
| |
| NavigateAndCommit(GURL("https://example.com")); |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/4)); |
| |
| ASSERT_TRUE( |
| CompletePrefetch(url_1, blink::mojom::SpeculationEagerness::kEager)); |
| ASSERT_TRUE( |
| CompletePrefetch(url_2, blink::mojom::SpeculationEagerness::kEager)); |
| |
| // Note: |url_3| is not prefetched as the limit for eager prefetches has been |
| // reached. |
| MakePrefetchOnMainFrame( |
| url_3, PrefetchType(/*use_prefetch_proxy=*/false, |
| blink::mojom::SpeculationEagerness::kEager)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(RequestCount(), 0); |
| Navigate(url_3, main_rfh()->GetFrameToken()); |
| ASSERT_FALSE(GetPrefetchToServe(url_3)); |
| |
| // We can still prefetch |url_4| as it is a conservative prefetch. |
| base::WeakPtr<PrefetchContainer> non_eager_prefetch = CompletePrefetch( |
| url_4, blink::mojom::SpeculationEagerness::kConservative); |
| ASSERT_TRUE(non_eager_prefetch); |
| EXPECT_EQ(non_eager_prefetch->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| |
| absl::optional<PrefetchReferringPageMetrics> referring_page_metrics = |
| PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh()); |
| EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 4); |
| EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 4); |
| EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 3); |
| } |
| |
| TEST_F(PrefetchServiceNewLimitsTest, NonEagerPrefetchEvictedAtLimit) { |
| const GURL url_1 = GURL("https://example.com/one"); |
| const GURL url_2 = GURL("https://example.com/two"); |
| const GURL url_3 = GURL("https://example.com/three"); |
| const GURL url_4 = GURL("https://example.com/four"); |
| |
| NavigateAndCommit(GURL("https://example.com")); |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/4)); |
| |
| base::MockRepeatingCallback<void(const GURL& url)> mock_eviction_callback; |
| EXPECT_CALL(mock_eviction_callback, Run(url_1)).Times(1); |
| EXPECT_CALL(mock_eviction_callback, Run(url_2)).Times(1); |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()) |
| ->SetPrefetchEvictionCallback(mock_eviction_callback.Get()); |
| |
| base::WeakPtr<PrefetchContainer> prefetch_1 = |
| CompletePrefetch(url_1, blink::mojom::SpeculationEagerness::kModerate); |
| ASSERT_TRUE(prefetch_1); |
| EXPECT_EQ(prefetch_1->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| |
| base::WeakPtr<PrefetchContainer> prefetch_2 = |
| CompletePrefetch(url_2, blink::mojom::SpeculationEagerness::kModerate); |
| ASSERT_TRUE(prefetch_2); |
| EXPECT_EQ(prefetch_2->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| ASSERT_TRUE(prefetch_1); |
| |
| base::WeakPtr<PrefetchContainer> prefetch_3 = |
| CompletePrefetch(url_3, blink::mojom::SpeculationEagerness::kModerate); |
| ASSERT_TRUE(prefetch_3); |
| EXPECT_EQ(prefetch_3->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| // Prefetch for |url_1| should have been evicted to allow a prefetch of |
| // |url_3|. |
| ASSERT_FALSE(prefetch_1); |
| ASSERT_TRUE(prefetch_2); |
| |
| base::WeakPtr<PrefetchContainer> prefetch_4 = |
| CompletePrefetch(url_4, blink::mojom::SpeculationEagerness::kModerate); |
| ASSERT_TRUE(prefetch_4); |
| EXPECT_EQ(prefetch_4->GetPrefetchStatus(), |
| PrefetchStatus::kPrefetchSuccessful); |
| // Prefetch for |url_2| should have been evicted to allow a prefetch of |
| // |url_4|. |
| ASSERT_FALSE(prefetch_2); |
| ASSERT_TRUE(prefetch_3); |
| |
| // The first and second prefetches should have failure reason set to |
| // 'kPrefetchEvicted'. |
| { |
| const auto source_id = ForceLogsUploadAndGetUkmId(); |
| auto actual_attempts = test_ukm_recorder()->GetEntries( |
| ukm::builders::Preloading_Attempt::kEntryName, |
| test::kPreloadingAttemptUkmMetrics); |
| EXPECT_EQ(actual_attempts.size(), 4u); |
| |
| std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts = |
| {attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| content::PrefetchStatus::kPrefetchEvicted), |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime), |
| attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kFailure, |
| ToPreloadingFailureReason( |
| content::PrefetchStatus::kPrefetchEvicted), |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime), |
| attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime), |
| attempt_entry_builder()->BuildEntry( |
| source_id, PreloadingType::kPrefetch, |
| PreloadingEligibility::kEligible, |
| PreloadingHoldbackStatus::kAllowed, |
| PreloadingTriggeringOutcome::kReady, |
| PreloadingFailureReason::kUnspecified, |
| /*accurate=*/false, |
| /*ready_time=*/ |
| base::ScopedMockElapsedTimersForTest::kMockElapsedTime)}; |
| EXPECT_THAT(actual_attempts, |
| testing::UnorderedElementsAreArray(expected_attempts)) |
| << test::ActualVsExpectedUkmEntriesToString(actual_attempts, |
| expected_attempts); |
| } |
| } |
| |
| TEST_F(PrefetchServiceNewLimitsTest, PrefetchWithNoCandidateIsNotStarted) { |
| const GURL url_1 = GURL("https://example.com/one"); |
| const GURL url_2 = GURL("https://example.com/two"); |
| const GURL url_3 = GURL("https://example.com/three"); |
| |
| NavigateAndCommit(GURL("https://example.com")); |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/3)); |
| |
| auto candidate_1 = blink::mojom::SpeculationCandidate::New(); |
| candidate_1->url = url_1; |
| candidate_1->action = blink::mojom::SpeculationAction::kPrefetch; |
| candidate_1->eagerness = blink::mojom::SpeculationEagerness::kEager; |
| candidate_1->referrer = blink::mojom::Referrer::New(); |
| auto candidate_2 = candidate_1.Clone(); |
| candidate_2->url = url_2; |
| auto candidate_3 = candidate_1.Clone(); |
| candidate_3->url = url_3; |
| |
| auto* prefetch_document_manager = |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()); |
| ASSERT_TRUE(prefetch_document_manager); |
| |
| base::MockRepeatingCallback<void(const GURL& url)> mock_eviction_callback; |
| EXPECT_CALL(mock_eviction_callback, Run(url_2)).Times(1); |
| prefetch_document_manager->SetPrefetchEvictionCallback( |
| mock_eviction_callback.Get()); |
| |
| // Send 3 candidates to PrefetchDocumentManager. |
| std::vector<blink::mojom::SpeculationCandidatePtr> candidates; |
| candidates.push_back(candidate_1.Clone()); |
| candidates.push_back(candidate_2.Clone()); |
| candidates.push_back(candidate_3.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| base::RunLoop().RunUntilIdle(); |
| VerifyCommonRequestState(url_1, /*use_prefetch_proxy=*/false); |
| |
| // Remove |url_2| from the list of candidates while a prefetch for |url_1| is |
| // in progress. |
| candidates.clear(); |
| candidates.push_back(candidate_1.Clone()); |
| candidates.push_back(candidate_3.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| |
| // Finish prefetch of |url_1|. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| // PrefetchService skips |url_2| because its candidate was removed, and starts |
| // prefetching |url_3| instead. |
| VerifyCommonRequestState(url_3, /*use_prefetch_proxy=*/false); |
| // Finish prefetch of |url_2|. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| // There should be no pending prefetch requests. |
| EXPECT_EQ(RequestCount(), 0); |
| } |
| |
| TEST_F(PrefetchServiceNewLimitsTest, |
| InProgressPrefetchWithNoCandidateIsCancelled) { |
| const GURL url_1 = GURL("https://example.com/one"); |
| const GURL url_2 = GURL("https://example.com/two"); |
| |
| NavigateAndCommit(GURL("https://example.com")); |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/2)); |
| |
| auto candidate_1 = blink::mojom::SpeculationCandidate::New(); |
| candidate_1->url = url_1; |
| candidate_1->action = blink::mojom::SpeculationAction::kPrefetch; |
| candidate_1->eagerness = blink::mojom::SpeculationEagerness::kEager; |
| candidate_1->referrer = blink::mojom::Referrer::New(); |
| auto candidate_2 = candidate_1.Clone(); |
| candidate_2->url = url_2; |
| |
| auto* prefetch_document_manager = |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()); |
| ASSERT_TRUE(prefetch_document_manager); |
| |
| base::MockRepeatingCallback<void(const GURL& url)> mock_eviction_callback; |
| EXPECT_CALL(mock_eviction_callback, Run(url_1)).Times(1); |
| prefetch_document_manager->SetPrefetchEvictionCallback( |
| mock_eviction_callback.Get()); |
| |
| // Send 2 candidates to PrefetchDocumentManager. |
| std::vector<blink::mojom::SpeculationCandidatePtr> candidates; |
| candidates.push_back(candidate_1.Clone()); |
| candidates.push_back(candidate_2.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Prefetch for |url_1| should have started. |
| VerifyCommonRequestState(url_1, /*use_prefetch_proxy=*/false); |
| |
| // Remove |candidate_1|. |
| candidates.clear(); |
| candidates.push_back(candidate_2.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The prefetch for |url_1| should be cancelled, and prefetch for |url_2| |
| // should have started. |
| |
| EXPECT_EQ(test_url_loader_factory_.pending_requests()->size(), 2u); |
| // The client for the first request should be disconnected. |
| EXPECT_FALSE( |
| test_url_loader_factory_.GetPendingRequest(0)->client.is_connected()); |
| // Clears out first request. |
| MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType, |
| /*use_prefetch_proxy=*/false, |
| {{"X-Testing", "Hello World"}}, kHTMLBody); |
| VerifyCommonRequestState(url_2, /*use_prefetch_proxy=*/false); |
| base::WeakPtr<PrefetchContainer> serveable_prefetch_container = |
| GetPrefetchToServe(url_1); |
| EXPECT_FALSE(serveable_prefetch_container); |
| } |
| |
| TEST_F(PrefetchServiceNewLimitsTest, |
| CompletedPrefetchWithNoCandidateIsEvicted) { |
| const GURL url_1 = GURL("https://example.com/one"); |
| const GURL url_2 = GURL("https://example.com/two"); |
| const GURL url_3 = GURL("https://example.com/three"); |
| |
| NavigateAndCommit(GURL("https://example.com")); |
| |
| MakePrefetchService( |
| std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>( |
| /*num_on_prefetch_likely_calls=*/2)); |
| |
| auto candidate_1 = blink::mojom::SpeculationCandidate::New(); |
| candidate_1->url = url_1; |
| candidate_1->action = blink::mojom::SpeculationAction::kPrefetch; |
| candidate_1->eagerness = blink::mojom::SpeculationEagerness::kEager; |
| candidate_1->referrer = blink::mojom::Referrer::New(); |
| auto candidate_2 = candidate_1.Clone(); |
| candidate_2->url = url_2; |
| |
| auto* prefetch_document_manager = |
| PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh()); |
| ASSERT_TRUE(prefetch_document_manager); |
| |
| base::MockRepeatingCallback<void(const GURL& url)> mock_eviction_callback; |
| EXPECT_CALL(mock_eviction_callback, Run(url_1)).Times(1); |
| prefetch_document_manager->SetPrefetchEvictionCallback( |
| mock_eviction_callback.Get()); |
| |
| // Send 2 candidates to PrefetchDocumentManager. |
| std::vector<blink::mojom::SpeculationCandidatePtr> candidates; |
| candidates.push_back(candidate_1.Clone()); |
| candidates.push_back(candidate_2.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Complete prefetches for |url_1| and |url_2|. |
| base::WeakPtr<PrefetchContainer> prefetch_1 = CompleteExistingPrefetch(url_1); |
| ASSERT_TRUE(prefetch_1); |
| base::WeakPtr<PrefetchContainer> prefetch_2 = CompleteExistingPrefetch(url_2); |
| ASSERT_TRUE(prefetch_2); |
| |
| // Remove |candidate_1|. |
| candidates.clear(); |
| candidates.push_back(candidate_2.Clone()); |
| prefetch_document_manager->ProcessCandidates(candidates, |
| /*devtools_observer=*/nullptr); |
| base::RunLoop().RunUntilIdle(); |
| // |prefetch_1| should have been removed. |
| EXPECT_FALSE(prefetch_1); |
| EXPECT_TRUE(prefetch_2); |
| } |
| |
| } // namespace |
| } // namespace content |