| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/format_macros.h" |
| #include "base/macros.h" |
| #include "base/metrics/field_trial_param_associator.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/net/prediction_options.h" |
| #include "chrome/browser/predictors/loading_predictor.h" |
| #include "chrome/browser/predictors/loading_predictor_factory.h" |
| #include "chrome/browser/predictors/loading_test_util.h" |
| #include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h" |
| #include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_manager_delegate.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/content_settings/core/browser/cookie_settings.h" |
| #include "components/content_settings/core/common/pref_names.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_field_trial.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_handle.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_link_manager.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" |
| #include "components/no_state_prefetch/common/no_state_prefetch_utils.h" |
| #include "components/no_state_prefetch/common/prerender_origin.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_utils.h" |
| #include "mojo/public/cpp/bindings/unique_receiver_set.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/http/http_cache.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "url/gurl.h" |
| |
| using base::Time; |
| using base::TimeDelta; |
| using base::TimeTicks; |
| using content::Referrer; |
| |
| namespace prerender { |
| |
| class UnitTestNoStatePrefetchManager; |
| |
| namespace { |
| |
| class DummyNoStatePrefetchContents : public NoStatePrefetchContents { |
| public: |
| DummyNoStatePrefetchContents( |
| UnitTestNoStatePrefetchManager* test_no_state_prefetch_manager, |
| const GURL& url, |
| Origin origin, |
| const absl::optional<url::Origin>& initiator_origin, |
| FinalStatus expected_final_status); |
| |
| ~DummyNoStatePrefetchContents() override; |
| |
| void StartPrerendering( |
| const gfx::Rect& bounds, |
| content::SessionStorageNamespace* session_storage_namespace) override; |
| |
| FinalStatus expected_final_status() const { return expected_final_status_; } |
| |
| bool prerendering_has_been_cancelled() const { |
| return NoStatePrefetchContents::prerendering_has_been_cancelled(); |
| } |
| |
| private: |
| static int g_next_route_id_; |
| int route_id_; |
| |
| UnitTestNoStatePrefetchManager* test_no_state_prefetch_manager_; |
| FinalStatus expected_final_status_; |
| }; |
| |
| class TestNetworkBytesChangedObserver |
| : public prerender::NoStatePrefetchHandle::Observer { |
| public: |
| TestNetworkBytesChangedObserver() : network_bytes_changed_(false) {} |
| |
| TestNetworkBytesChangedObserver(const TestNetworkBytesChangedObserver&) = |
| delete; |
| TestNetworkBytesChangedObserver& operator=( |
| const TestNetworkBytesChangedObserver&) = delete; |
| |
| // prerender::NoStatePrefetchHandle::Observer |
| void OnPrefetchStop( |
| NoStatePrefetchHandle* no_state_prefetch_handle) override {} |
| void OnPrefetchNetworkBytesChanged( |
| NoStatePrefetchHandle* no_state_prefetch_handle) override { |
| network_bytes_changed_ = true; |
| } |
| |
| bool network_bytes_changed() const { return network_bytes_changed_; } |
| |
| private: |
| bool network_bytes_changed_; |
| }; |
| |
| int DummyNoStatePrefetchContents::g_next_route_id_ = 0; |
| |
| const gfx::Size kDefaultViewSize(640, 480); |
| |
| } // namespace |
| |
| class UnitTestNoStatePrefetchManager : public NoStatePrefetchManager { |
| public: |
| using NoStatePrefetchManager::kNavigationRecordWindowMs; |
| |
| explicit UnitTestNoStatePrefetchManager(Profile* profile) |
| : NoStatePrefetchManager( |
| profile, |
| std::make_unique<ChromeNoStatePrefetchManagerDelegate>(profile)) { |
| set_rate_limit_enabled(false); |
| } |
| |
| ~UnitTestNoStatePrefetchManager() override {} |
| |
| // From KeyedService, via PrererenderManager: |
| void Shutdown() override { |
| if (next_no_state_prefetch_contents()) |
| next_no_state_prefetch_contents_->Destroy(FINAL_STATUS_PROFILE_DESTROYED); |
| NoStatePrefetchManager::Shutdown(); |
| } |
| |
| // From NoStatePrefetchManager: |
| void MoveEntryToPendingDelete(NoStatePrefetchContents* entry, |
| FinalStatus final_status) override { |
| if (entry == next_no_state_prefetch_contents_.get()) |
| return; |
| NoStatePrefetchManager::MoveEntryToPendingDelete(entry, final_status); |
| } |
| |
| NoStatePrefetchContents* FindEntry(const GURL& url) { |
| DeleteOldEntries(); |
| to_delete_prefetches_.clear(); |
| NoStatePrefetchData* data = FindNoStatePrefetchData(url, nullptr); |
| return data ? data->contents() : nullptr; |
| } |
| |
| std::unique_ptr<NoStatePrefetchContents> FindAndUseEntry(const GURL& url) { |
| NoStatePrefetchData* no_state_prefetch_data = |
| FindNoStatePrefetchData(url, nullptr); |
| if (!no_state_prefetch_data) |
| return nullptr; |
| auto to_erase = FindIteratorForNoStatePrefetchContents( |
| no_state_prefetch_data->contents()); |
| CHECK(to_erase != active_prefetches_.end()); |
| std::unique_ptr<NoStatePrefetchContents> no_state_prefetch_contents = |
| no_state_prefetch_data->ReleaseContents(); |
| active_prefetches_.erase(to_erase); |
| |
| no_state_prefetch_contents->MarkAsUsedForTesting(); |
| return no_state_prefetch_contents; |
| } |
| |
| DummyNoStatePrefetchContents* CreateNextNoStatePrefetchContents( |
| const GURL& url, |
| FinalStatus expected_final_status) { |
| return SetNextNoStatePrefetchContents( |
| std::make_unique<DummyNoStatePrefetchContents>( |
| this, url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, |
| url::Origin::Create(GURL("https://uniquedifferentorigin.com")), |
| expected_final_status)); |
| } |
| |
| DummyNoStatePrefetchContents* CreateNextNoStatePrefetchContents( |
| const GURL& url, |
| const absl::optional<url::Origin>& initiator_origin, |
| Origin origin, |
| FinalStatus expected_final_status) { |
| return SetNextNoStatePrefetchContents( |
| std::make_unique<DummyNoStatePrefetchContents>( |
| this, url, origin, initiator_origin, expected_final_status)); |
| } |
| |
| DummyNoStatePrefetchContents* CreateNextNoStatePrefetchContents( |
| const GURL& url, |
| const std::vector<GURL>& alias_urls, |
| FinalStatus expected_final_status) { |
| auto no_state_prefetch_contents = |
| std::make_unique<DummyNoStatePrefetchContents>( |
| this, url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, |
| url::Origin::Create(GURL("https://uniquedifferentorigin.com")), |
| expected_final_status); |
| for (const GURL& alias : alias_urls) |
| EXPECT_TRUE(no_state_prefetch_contents->AddAliasURL(alias)); |
| return SetNextNoStatePrefetchContents( |
| std::move(no_state_prefetch_contents)); |
| } |
| |
| void set_rate_limit_enabled(bool enabled) { |
| mutable_config().rate_limit_enabled = enabled; |
| } |
| |
| NoStatePrefetchContents* next_no_state_prefetch_contents() { |
| return next_no_state_prefetch_contents_.get(); |
| } |
| |
| NoStatePrefetchContents* GetNoStatePrefetchContentsForRoute( |
| int child_id, |
| int route_id) const override { |
| // Overridden for the NoStatePrefetchLinkManager's pending prefetch logic. |
| auto it = no_state_prefetch_contents_map_.find( |
| std::make_pair(child_id, route_id)); |
| return it != no_state_prefetch_contents_map_.end() ? it->second : nullptr; |
| } |
| |
| void DummyNoStatePrefetchContentsStarted( |
| int child_id, |
| int route_id, |
| NoStatePrefetchContents* no_state_prefetch_contents) { |
| no_state_prefetch_contents_map_[std::make_pair(child_id, route_id)] = |
| no_state_prefetch_contents; |
| } |
| |
| void DummyNoStatePrefetchContentsDestroyed(int child_id, int route_id) { |
| no_state_prefetch_contents_map_.erase(std::make_pair(child_id, route_id)); |
| } |
| |
| void SetIsLowEndDevice(bool is_low_end_device) { |
| is_low_end_device_ = is_low_end_device; |
| } |
| |
| private: |
| bool IsLowEndDevice() const override { return is_low_end_device_; } |
| |
| DummyNoStatePrefetchContents* SetNextNoStatePrefetchContents( |
| std::unique_ptr<DummyNoStatePrefetchContents> |
| no_state_prefetch_contents) { |
| CHECK(!next_no_state_prefetch_contents_); |
| DummyNoStatePrefetchContents* contents_ptr = |
| no_state_prefetch_contents.get(); |
| next_no_state_prefetch_contents_ = std::move(no_state_prefetch_contents); |
| return contents_ptr; |
| } |
| |
| std::unique_ptr<NoStatePrefetchContents> CreateNoStatePrefetchContents( |
| const GURL& url, |
| const Referrer& referrer, |
| const absl::optional<url::Origin>& initiator_origin, |
| Origin origin) override { |
| CHECK(next_no_state_prefetch_contents_); |
| EXPECT_EQ(url, next_no_state_prefetch_contents_->prerender_url()); |
| EXPECT_EQ(origin, next_no_state_prefetch_contents_->origin()); |
| return std::move(next_no_state_prefetch_contents_); |
| } |
| |
| // Maintain a map from route pairs to NoStatePrefetchContents for |
| // GetNoStatePrefetchContentsForRoute. |
| using NoStatePrefetchContentsMap = |
| std::map<std::pair<int, int>, NoStatePrefetchContents*>; |
| NoStatePrefetchContentsMap no_state_prefetch_contents_map_; |
| |
| std::unique_ptr<NoStatePrefetchContents> next_no_state_prefetch_contents_; |
| bool is_low_end_device_; |
| }; |
| |
| class MockNetworkChangeNotifier4GMetered : public net::NetworkChangeNotifier { |
| public: |
| ConnectionType GetCurrentConnectionType() const override { |
| return NetworkChangeNotifier::CONNECTION_4G; |
| } |
| |
| ConnectionCost GetCurrentConnectionCost() override { |
| return NetworkChangeNotifier::CONNECTION_COST_METERED; |
| } |
| }; |
| |
| class MockNetworkChangeNotifier4GUnmetered : public net::NetworkChangeNotifier { |
| public: |
| ConnectionType GetCurrentConnectionType() const override { |
| return NetworkChangeNotifier::CONNECTION_4G; |
| } |
| |
| ConnectionCost GetCurrentConnectionCost() override { |
| return NetworkChangeNotifier::CONNECTION_COST_UNMETERED; |
| } |
| }; |
| |
| class MockNetworkChangeNotifierWifiMetered : public net::NetworkChangeNotifier { |
| public: |
| ConnectionType GetCurrentConnectionType() const override { |
| return NetworkChangeNotifier::CONNECTION_WIFI; |
| } |
| |
| ConnectionCost GetCurrentConnectionCost() override { |
| return NetworkChangeNotifier::CONNECTION_COST_METERED; |
| } |
| }; |
| |
| DummyNoStatePrefetchContents::DummyNoStatePrefetchContents( |
| UnitTestNoStatePrefetchManager* test_no_state_prefetch_manager, |
| const GURL& url, |
| Origin origin, |
| const absl::optional<url::Origin>& initiator_origin, |
| FinalStatus expected_final_status) |
| : NoStatePrefetchContents( |
| std::make_unique<ChromeNoStatePrefetchContentsDelegate>(), |
| test_no_state_prefetch_manager, |
| nullptr, |
| url, |
| Referrer(), |
| initiator_origin, |
| origin), |
| route_id_(g_next_route_id_++), |
| test_no_state_prefetch_manager_(test_no_state_prefetch_manager), |
| expected_final_status_(expected_final_status) {} |
| |
| DummyNoStatePrefetchContents::~DummyNoStatePrefetchContents() { |
| EXPECT_EQ(expected_final_status_, final_status()); |
| test_no_state_prefetch_manager_->DummyNoStatePrefetchContentsDestroyed( |
| -1, route_id_); |
| } |
| |
| void DummyNoStatePrefetchContents::StartPrerendering( |
| const gfx::Rect& bounds, |
| content::SessionStorageNamespace* session_storage_namespace) { |
| load_start_time_ = test_no_state_prefetch_manager_->GetCurrentTimeTicks(); |
| prerendering_has_started_ = true; |
| test_no_state_prefetch_manager_->DummyNoStatePrefetchContentsStarted( |
| -1, route_id_, this); |
| NotifyPrefetchStart(); |
| } |
| |
| class PrerenderTest : public testing::Test { |
| public: |
| static const int kDefaultChildId = -1; |
| static const int kDefaultRenderViewRouteId = -1; |
| |
| PrerenderTest() |
| : no_state_prefetch_manager_( |
| new UnitTestNoStatePrefetchManager(&profile_)), |
| no_state_prefetch_link_manager_( |
| new NoStatePrefetchLinkManager(no_state_prefetch_manager_.get())) { |
| no_state_prefetch_manager()->SetIsLowEndDevice(false); |
| } |
| |
| ~PrerenderTest() override { |
| no_state_prefetch_link_manager_->Shutdown(); |
| no_state_prefetch_manager_->Shutdown(); |
| } |
| |
| void TearDown() override { |
| base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); |
| } |
| |
| base::SimpleTestTickClock* tick_clock() { return &tick_clock_; } |
| |
| UnitTestNoStatePrefetchManager* no_state_prefetch_manager() { |
| return no_state_prefetch_manager_.get(); |
| } |
| |
| NoStatePrefetchLinkManager* no_state_prefetch_link_manager() { |
| return no_state_prefetch_link_manager_.get(); |
| } |
| |
| Profile* profile() { return &profile_; } |
| |
| void SetConcurrency(size_t concurrency) { |
| no_state_prefetch_manager() |
| ->mutable_config() |
| .max_link_concurrency_per_launcher = concurrency; |
| no_state_prefetch_manager()->mutable_config().max_link_concurrency = |
| std::max( |
| no_state_prefetch_manager()->mutable_config().max_link_concurrency, |
| concurrency); |
| } |
| |
| bool IsEmptyNoStatePrefetchLinkManager() const { |
| return no_state_prefetch_link_manager_->IsEmpty(); |
| } |
| |
| size_t CountExistingTriggers() { |
| return no_state_prefetch_link_manager()->triggers_.size(); |
| } |
| |
| bool LastTriggerExists() { |
| return no_state_prefetch_link_manager()->triggers_.begin() != |
| no_state_prefetch_link_manager()->triggers_.end(); |
| } |
| |
| bool LastTriggerIsRunning() { |
| CHECK(LastTriggerExists()); |
| return no_state_prefetch_link_manager()->TriggerIsRunningForTesting( |
| no_state_prefetch_link_manager()->triggers_.back().get()); |
| } |
| |
| bool AddLinkTrigger(const GURL& url, |
| const GURL& initiator_url, |
| int render_process_id, |
| int render_view_id) { |
| auto attributes = blink::mojom::PrerenderAttributes::New(); |
| attributes->url = url; |
| attributes->trigger_type = |
| blink::mojom::PrerenderTriggerType::kLinkRelPrerender; |
| attributes->referrer = blink::mojom::Referrer::New( |
| initiator_url, network::mojom::ReferrerPolicy::kDefault); |
| attributes->view_size = kDefaultViewSize; |
| |
| // This could delete an existing prefetcher as a side-effect. |
| absl::optional<int> link_trigger_id = |
| no_state_prefetch_link_manager()->OnStartLinkTrigger( |
| render_process_id, render_view_id, std::move(attributes), |
| url::Origin::Create(initiator_url)); |
| |
| // Check if the new prefetch request was added and running. |
| return link_trigger_id && LastTriggerIsRunning(); |
| } |
| |
| // Shorthand to add a simple link trigger with a reasonable source. Returns |
| // true iff the prefetcher has been added to the NoStatePrefetchManager by the |
| // NoStatePrefetchLinkManager and the NoStatePrefetchManager returned a |
| // handle. |
| bool AddSimpleLinkTrigger(const GURL& url) { |
| return AddLinkTrigger(url, GURL(), kDefaultChildId, |
| kDefaultRenderViewRouteId); |
| } |
| |
| // Shorthand to add a simple link trigger with a reasonable source. Returns |
| // true iff the prefetcher has been added to the NoStatePrefetchManager by the |
| // NoStatePrefetchLinkManager and the NoStatePrefetchManager returned a |
| // handle. The referrer is set to a google domain. |
| bool AddSimpleGWSLinkTrigger(const GURL& url) { |
| return AddLinkTrigger(url, GURL("https://www.google.com"), kDefaultChildId, |
| kDefaultRenderViewRouteId); |
| } |
| |
| void AbandonFirstTrigger() { |
| CHECK(!no_state_prefetch_link_manager()->triggers_.empty()); |
| no_state_prefetch_link_manager()->OnAbandonLinkTrigger( |
| no_state_prefetch_link_manager()->triggers_.front()->link_trigger_id); |
| } |
| |
| void AbandonLastTrigger() { |
| CHECK(!no_state_prefetch_link_manager()->triggers_.empty()); |
| no_state_prefetch_link_manager()->OnAbandonLinkTrigger( |
| no_state_prefetch_link_manager()->triggers_.back()->link_trigger_id); |
| } |
| |
| void CancelFirstTrigger() { |
| CHECK(!no_state_prefetch_link_manager()->triggers_.empty()); |
| no_state_prefetch_link_manager()->OnCancelLinkTrigger( |
| no_state_prefetch_link_manager()->triggers_.front()->link_trigger_id); |
| } |
| |
| void CancelLastTrigger() { |
| CHECK(!no_state_prefetch_link_manager()->triggers_.empty()); |
| no_state_prefetch_link_manager()->OnCancelLinkTrigger( |
| no_state_prefetch_link_manager()->triggers_.back()->link_trigger_id); |
| } |
| |
| void DisablePrerender() { |
| profile_.GetPrefs()->SetInteger( |
| prefs::kNetworkPredictionOptions, |
| chrome_browser_net::NETWORK_PREDICTION_NEVER); |
| } |
| |
| void EnablePrerender() { |
| profile_.GetPrefs()->SetInteger( |
| prefs::kNetworkPredictionOptions, |
| chrome_browser_net::NETWORK_PREDICTION_ALWAYS); |
| } |
| |
| const base::HistogramTester& histogram_tester() { return histogram_tester_; } |
| |
| protected: |
| // This needs to be initialized before any tasks running on other threads |
| // access the feature list, and destroyed after |task_environment_|, to avoid |
| // data races. |
| base::test::ScopedFeatureList feature_list_; |
| |
| private: |
| // Needed to pass NoStatePrefetchManager's DCHECKs. |
| content::BrowserTaskEnvironment task_environment_; |
| |
| TestingProfile profile_; |
| base::SimpleTestTickClock tick_clock_; |
| std::unique_ptr<UnitTestNoStatePrefetchManager> no_state_prefetch_manager_; |
| std::unique_ptr<NoStatePrefetchLinkManager> no_state_prefetch_link_manager_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| TEST_F(PrerenderTest, RespectsThirdPartyCookiesPref) { |
| GURL url("http://www.google.com/"); |
| profile()->GetPrefs()->SetInteger( |
| prefs::kCookieControlsMode, |
| static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty)); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| histogram_tester().ExpectUniqueSample( |
| "Prerender.FinalStatus", FINAL_STATUS_BLOCK_THIRD_PARTY_COOKIES, 1); |
| } |
| |
| class PrerenderGWSPrefetchHoldbackTest : public PrerenderTest { |
| public: |
| PrerenderGWSPrefetchHoldbackTest() { |
| feature_list_.InitAndEnableFeature(kGWSPrefetchHoldback); |
| } |
| }; |
| |
| TEST_F(PrerenderGWSPrefetchHoldbackTest, GWSPrefetchHoldbackNonGWSSReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| } |
| |
| TEST_F(PrerenderGWSPrefetchHoldbackTest, GWSPrefetchHoldbackGWSReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("www.google.com")), ORIGIN_GWS_PRERENDER, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_FALSE(AddSimpleGWSLinkTrigger(url)); |
| } |
| |
| class PrerenderGWSPrefetchHoldbackOffTest : public PrerenderTest { |
| public: |
| PrerenderGWSPrefetchHoldbackOffTest() { |
| feature_list_.InitAndDisableFeature(kGWSPrefetchHoldback); |
| } |
| }; |
| |
| TEST_F(PrerenderGWSPrefetchHoldbackOffTest, |
| GWSPrefetchHoldbackOffNonGWSReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| } |
| |
| TEST_F(PrerenderGWSPrefetchHoldbackOffTest, GWSPrefetchHoldbackOffGWSReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("www.google.com")), ORIGIN_GWS_PRERENDER, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(AddSimpleGWSLinkTrigger(url)); |
| } |
| |
| class PrerendererNavigationPredictorPrefetchHoldbackTest |
| : public PrerenderTest { |
| public: |
| PrerendererNavigationPredictorPrefetchHoldbackTest() { |
| feature_list_.InitAndEnableFeature(kNavigationPredictorPrefetchHoldback); |
| } |
| }; |
| |
| TEST_F(PrerendererNavigationPredictorPrefetchHoldbackTest, |
| PredictorPrefetchHoldbackNonPredictorReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("www.notgoogle.com")), |
| ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| } |
| |
| TEST_F(PrerendererNavigationPredictorPrefetchHoldbackTest, |
| PredictorPrefetchHoldbackPredictorReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_NAVIGATION_PREDICTOR, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| EXPECT_EQ(nullptr, |
| no_state_prefetch_manager()->AddPrerenderFromNavigationPredictor( |
| url, nullptr, gfx::Size())); |
| } |
| |
| // Verify that link-rel:next URLs are not prefetched. |
| TEST_F(PrerenderTest, LinkRelNextWithNSPDisabled) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("www.notgoogle.com")), ORIGIN_LINK_REL_NEXT, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| EXPECT_EQ( |
| nullptr, |
| no_state_prefetch_manager()->AddPrerenderWithPreconnectFallbackForTesting( |
| ORIGIN_LINK_REL_NEXT, url, |
| url::Origin::Create(GURL("www.notgoogle.com")))); |
| histogram_tester().ExpectUniqueSample( |
| "Prerender.FinalStatus", FINAL_STATUS_LINK_REL_NEXT_NOT_ALLOWED, 1); |
| } |
| |
| class PrerendererNavigationPredictorPrefetchHoldbackDisabledTest |
| : public PrerenderTest { |
| public: |
| PrerendererNavigationPredictorPrefetchHoldbackDisabledTest() { |
| feature_list_.InitAndDisableFeature(kNavigationPredictorPrefetchHoldback); |
| } |
| }; |
| |
| TEST_F(PrerendererNavigationPredictorPrefetchHoldbackDisabledTest, |
| PredictorPrefetchHoldbackOffNonPredictorReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("www.notgoogle.com")), |
| ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| } |
| |
| TEST_F(PrerendererNavigationPredictorPrefetchHoldbackDisabledTest, |
| PredictorPrefetchHoldbackOffPredictorReferrer) { |
| GURL url("http://www.notgoogle.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_NAVIGATION_PREDICTOR, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_NE(nullptr, |
| no_state_prefetch_manager()->AddPrerenderFromNavigationPredictor( |
| url, nullptr, gfx::Size())); |
| } |
| |
| // Flaky on Android and Mac, crbug.com/1087876. |
| TEST_F(PrerenderTest, DISABLED_PrerenderDisabledOnLowEndDevice) { |
| GURL url("http://www.google.com/"); |
| no_state_prefetch_manager()->SetIsLowEndDevice(true); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| histogram_tester().ExpectUniqueSample("Prerender.FinalStatus", |
| FINAL_STATUS_LOW_END_DEVICE, 1); |
| } |
| |
| TEST_F(PrerenderTest, FoundTest) { |
| base::TimeDelta prefetch_age; |
| FinalStatus final_status; |
| Origin origin; |
| |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| |
| EXPECT_TRUE(no_state_prefetch_manager()->GetPrefetchInformation( |
| url, &prefetch_age, &final_status, &origin)); |
| EXPECT_EQ(prerender::FINAL_STATUS_UNKNOWN, final_status); |
| EXPECT_EQ(prerender::ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, origin); |
| |
| const base::TimeDelta advance_duration = TimeDelta::FromSeconds(1); |
| tick_clock()->Advance(advance_duration); |
| EXPECT_TRUE(no_state_prefetch_manager()->GetPrefetchInformation( |
| url, &prefetch_age, &final_status, &origin)); |
| EXPECT_LE(advance_duration, prefetch_age); |
| EXPECT_EQ(prerender::FINAL_STATUS_UNKNOWN, final_status); |
| EXPECT_EQ(prerender::ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, origin); |
| |
| no_state_prefetch_manager()->ClearPrefetchInformationForTesting(); |
| EXPECT_FALSE(no_state_prefetch_manager()->GetPrefetchInformation( |
| url, &prefetch_age, &final_status, &origin)); |
| } |
| |
| // Flaky on Android, crbug.com/1088454. |
| // Make sure that if queue a request, and a second prerender request for the |
| // same URL comes in, that the second request attaches to the first prerender, |
| // and we don't use the second prerender contents. |
| // This test is the same as the "DuplicateTest" above, but for NoStatePrefetch. |
| TEST_F(PrerenderTest, DISABLED_DuplicateTest_NoStatePrefetch) { |
| SetConcurrency(2); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| |
| DummyNoStatePrefetchContents* no_state_prefetch_contents1 = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_PROFILE_DESTROYED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_EQ(no_state_prefetch_contents1, |
| no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| EXPECT_FALSE(no_state_prefetch_contents1->prerendering_has_started()); |
| |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Ensure that we expire a prerendered page after the max. permitted time. |
| TEST_F(PrerenderTest, ExpireTest) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| tick_clock()->Advance(no_state_prefetch_manager()->config().time_to_live + |
| TimeDelta::FromSeconds(1)); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // Ensure that we don't launch prerenders of bad urls (in this case, a mailto: |
| // url) |
| TEST_F(PrerenderTest, BadURLTest) { |
| GURL url("mailto:test@gmail.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_UNSUPPORTED_SCHEME); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // When the user navigates away from a page, the prerenders it launched should |
| // have their time to expiry shortened from the default time to live. |
| TEST_F(PrerenderTest, LinkManagerNavigateAwayExpire) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| const TimeDelta time_to_live = TimeDelta::FromSeconds(300); |
| const TimeDelta abandon_time_to_live = TimeDelta::FromSeconds(20); |
| const TimeDelta test_advance = TimeDelta::FromSeconds(22); |
| ASSERT_LT(test_advance, time_to_live); |
| ASSERT_LT(abandon_time_to_live, test_advance); |
| |
| no_state_prefetch_manager()->mutable_config().time_to_live = time_to_live; |
| no_state_prefetch_manager()->mutable_config().abandon_time_to_live = |
| abandon_time_to_live; |
| |
| GURL url("http://example.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| AbandonLastTrigger(); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| tick_clock()->Advance(test_advance); |
| |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // But when we navigate away very close to the original expiry of a prerender, |
| // we shouldn't expect it to be extended. |
| TEST_F(PrerenderTest, LinkManagerNavigateAwayNearExpiry) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| const TimeDelta time_to_live = TimeDelta::FromSeconds(300); |
| const TimeDelta abandon_time_to_live = TimeDelta::FromSeconds(20); |
| |
| // We will expect the prerender to still be alive after advancing the clock |
| // by first_advance. But, after second_advance, we expect it to have timed |
| // out, demonstrating that you can't extend a prerender by navigating away |
| // from its launcher. |
| const TimeDelta first_advance = TimeDelta::FromSeconds(298); |
| const TimeDelta second_advance = TimeDelta::FromSeconds(4); |
| ASSERT_LT(first_advance, time_to_live); |
| ASSERT_LT(time_to_live - first_advance, abandon_time_to_live); |
| ASSERT_LT(time_to_live, first_advance + second_advance); |
| |
| no_state_prefetch_manager()->mutable_config().time_to_live = time_to_live; |
| no_state_prefetch_manager()->mutable_config().abandon_time_to_live = |
| abandon_time_to_live; |
| |
| GURL url("http://example2.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| |
| tick_clock()->Advance(first_advance); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| |
| AbandonLastTrigger(); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| |
| EXPECT_FALSE(no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| |
| tick_clock()->Advance(second_advance); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // When the user navigates away from a page, and then launches a new prerender, |
| // the new prerender should preempt the abandoned prerender even if the |
| // abandoned prerender hasn't expired. |
| TEST_F(PrerenderTest, LinkManagerNavigateAwayLaunchAnother) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| const TimeDelta time_to_live = TimeDelta::FromSeconds(300); |
| const TimeDelta abandon_time_to_live = TimeDelta::FromSeconds(20); |
| const TimeDelta test_advance = TimeDelta::FromSeconds(5); |
| ASSERT_LT(test_advance, time_to_live); |
| ASSERT_GT(abandon_time_to_live, test_advance); |
| |
| no_state_prefetch_manager()->mutable_config().time_to_live = time_to_live; |
| no_state_prefetch_manager()->mutable_config().abandon_time_to_live = |
| abandon_time_to_live; |
| |
| GURL url("http://example.com"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| AbandonLastTrigger(); |
| |
| tick_clock()->Advance(test_advance); |
| |
| GURL second_url("http://example2.com"); |
| DummyNoStatePrefetchContents* second_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| second_url, FINAL_STATUS_PROFILE_DESTROYED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(second_url)); |
| EXPECT_EQ(second_no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(second_url)); |
| } |
| |
| // Prefetching the same URL twice during |time_to_live| results in a duplicate |
| // and is aborted. |
| TEST_F(PrerenderTest, NoStatePrefetchDuplicate) { |
| const GURL kUrl("http://www.google.com/"); |
| predictors::LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| |
| auto* loading_predictor = |
| predictors::LoadingPredictorFactory::GetForProfile(profile()); |
| loading_predictor->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| |
| // Prefetch the url once. |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| kUrl, absl::nullopt, ORIGIN_OMNIBOX, FINAL_STATUS_CANCELLED); |
| EXPECT_TRUE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| kUrl, nullptr, gfx::Size())); |
| // Cancel the prerender so that it is not reused. |
| no_state_prefetch_manager()->CancelAllPrerenders(); |
| |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| kUrl, absl::nullopt, ORIGIN_OMNIBOX, FINAL_STATUS_PROFILE_DESTROYED); |
| |
| // Prefetching again before time_to_live aborts, because it is a duplicate. |
| tick_clock()->Advance(base::Seconds(1)); |
| EXPECT_FALSE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| kUrl, nullptr, gfx::Size())); |
| histogram_tester().ExpectBucketCount("Prerender.FinalStatus", |
| FINAL_STATUS_DUPLICATE, 1); |
| |
| // Prefetching after time_to_live succeeds. |
| tick_clock()->Advance(base::Minutes(net::HttpCache::kPrefetchReuseMins)); |
| EXPECT_TRUE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| kUrl, nullptr, gfx::Size())); |
| } |
| |
| // Make sure that if we prerender more requests than we support, that we launch |
| // them in the order given up until we reach MaxConcurrency, at which point we |
| // queue them and launch them in the order given. As well, insure that limits |
| // are enforced for the system as a whole and on a per launcher basis. |
| TEST_F(PrerenderTest, MaxConcurrencyTest) { |
| struct TestConcurrency { |
| size_t max_link_concurrency; |
| size_t max_link_concurrency_per_launcher; |
| }; |
| |
| const TestConcurrency concurrencies_to_test[] = { |
| {no_state_prefetch_manager()->config().max_link_concurrency, |
| no_state_prefetch_manager()->config().max_link_concurrency_per_launcher}, |
| |
| // With the system limit higher than the per launcher limit, the per |
| // launcher limit should be in effect. |
| {2, 1}, |
| |
| // With the per launcher limit higher than system limit, the system limit |
| // should be in effect. |
| {2, 4}, |
| }; |
| |
| size_t test_id = 0; |
| for (const TestConcurrency& current_test : concurrencies_to_test) { |
| test_id++; |
| no_state_prefetch_manager()->mutable_config().max_link_concurrency = |
| current_test.max_link_concurrency; |
| no_state_prefetch_manager() |
| ->mutable_config() |
| .max_link_concurrency_per_launcher = |
| current_test.max_link_concurrency_per_launcher; |
| |
| const size_t effective_max_link_concurrency = |
| std::min(current_test.max_link_concurrency, |
| current_test.max_link_concurrency_per_launcher); |
| |
| std::vector<GURL> urls; |
| std::vector<NoStatePrefetchContents*> no_state_prefetch_contentses; |
| |
| // Launch prerenders up to the maximum this launcher can support. |
| for (size_t j = 0; j < effective_max_link_concurrency; ++j) { |
| urls.push_back(GURL(base::StringPrintf( |
| "http://google.com/use#%" PRIuS "%" PRIuS, j, test_id))); |
| no_state_prefetch_contentses.push_back( |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| urls.back(), FINAL_STATUS_USED)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(urls.back())); |
| EXPECT_FALSE( |
| no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| EXPECT_TRUE( |
| no_state_prefetch_contentses.back()->prerendering_has_started()); |
| } |
| |
| if (current_test.max_link_concurrency > effective_max_link_concurrency) { |
| // We should be able to launch more prerenders on this system, but not for |
| // the default launcher. |
| GURL extra_url("http://google.com/extraurl"); |
| size_t trigger_count = CountExistingTriggers(); |
| EXPECT_FALSE(AddSimpleLinkTrigger(extra_url)); |
| EXPECT_EQ(trigger_count + 1, CountExistingTriggers()); |
| |
| CancelLastTrigger(); |
| EXPECT_EQ(trigger_count, CountExistingTriggers()); |
| } |
| |
| GURL url_to_delay( |
| base::StringPrintf("http://www.google.com/delayme#%" PRIuS, test_id)); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents_to_delay = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url_to_delay, FINAL_STATUS_USED); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url_to_delay)); |
| EXPECT_FALSE( |
| no_state_prefetch_contents_to_delay->prerendering_has_started()); |
| EXPECT_TRUE(no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url_to_delay)); |
| for (size_t j = 0; j < effective_max_link_concurrency; ++j) { |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(urls[j]); |
| EXPECT_EQ(no_state_prefetch_contentses[j], entry.get()); |
| EXPECT_TRUE( |
| no_state_prefetch_contents_to_delay->prerendering_has_started()); |
| } |
| |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url_to_delay); |
| EXPECT_EQ(no_state_prefetch_contents_to_delay, entry.get()); |
| EXPECT_FALSE( |
| no_state_prefetch_manager()->next_no_state_prefetch_contents()); |
| } |
| } |
| |
| // Flaky on Android: https://crbug.com/1105908 |
| TEST_F(PrerenderTest, DISABLED_AliasURLTest) { |
| SetConcurrency(7); |
| |
| GURL url("http://www.google.com/"); |
| GURL alias_url1("http://www.google.com/index.html"); |
| GURL alias_url2("http://google.com/"); |
| GURL not_an_alias_url("http://google.com/index.html"); |
| std::vector<GURL> alias_urls; |
| alias_urls.push_back(alias_url1); |
| alias_urls.push_back(alias_url2); |
| |
| // Test that all of the aliases work, but not_an_alias_url does not. |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, alias_urls, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(not_an_alias_url)); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(alias_url1); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| no_state_prefetch_manager()->ClearPrefetchInformationForTesting(); |
| |
| no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, alias_urls, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| entry = no_state_prefetch_manager()->FindAndUseEntry(alias_url2); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| no_state_prefetch_manager()->ClearPrefetchInformationForTesting(); |
| |
| no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, alias_urls, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| entry = no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| no_state_prefetch_manager()->ClearPrefetchInformationForTesting(); |
| |
| // Test that alias URLs can be added. |
| no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, alias_urls, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(alias_url1)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(alias_url2)); |
| entry = no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Tests that prerendering is cancelled when the source render view does not |
| // exist. On failure, the DCHECK in CreateNoStatePrefetchContents() above |
| // should be triggered. |
| TEST_F(PrerenderTest, SourceRenderViewClosed) { |
| GURL url("http://www.google.com/"); |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_PROFILE_DESTROYED); |
| AddLinkTrigger(url, url, 100, 200); |
| EXPECT_FALSE(LastTriggerExists()); |
| } |
| |
| // Tests that prerendering is cancelled when we launch a second prerender of |
| // the same target within a short time interval. |
| TEST_F(PrerenderTest, RecentlyVisited) { |
| GURL url("http://www.google.com/"); |
| |
| no_state_prefetch_manager()->RecordNavigation(url); |
| |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_RECENTLY_VISITED); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_started()); |
| } |
| |
| TEST_F(PrerenderTest, NotSoRecentlyVisited) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| GURL url("http://www.google.com/"); |
| |
| no_state_prefetch_manager()->RecordNavigation(url); |
| tick_clock()->Advance(TimeDelta::FromMilliseconds( |
| UnitTestNoStatePrefetchManager::kNavigationRecordWindowMs + 500)); |
| |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Tests that the prerender manager matches include the fragment. |
| TEST_F(PrerenderTest, FragmentMatchesTest) { |
| GURL fragment_url("http://www.google.com/#test"); |
| |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| fragment_url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(fragment_url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(fragment_url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Tests that the prerender manager uses fragment references when matching |
| // prerender URLs in the case a different fragment is in both URLs. |
| TEST_F(PrerenderTest, FragmentsDifferTest) { |
| GURL fragment_url("http://www.google.com/#test"); |
| GURL other_fragment_url("http://www.google.com/#other_test"); |
| |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| fragment_url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(fragment_url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(other_fragment_url)); |
| |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(fragment_url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Make sure that clearing works as expected. |
| TEST_F(PrerenderTest, ClearTest) { |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CACHE_OR_HISTORY_CLEARED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| no_state_prefetch_manager()->ClearData( |
| NoStatePrefetchManager::CLEAR_PRERENDER_CONTENTS); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // Make sure canceling works as expected. |
| TEST_F(PrerenderTest, CancelAllTest) { |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| no_state_prefetch_manager()->CancelAllPrerenders(); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // Test that when prerender is enabled, a prerender initiated by omnibox is |
| // successful. |
| TEST_F(PrerenderTest, OmniboxAllowedWhenNotDisabled) { |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| GURL("http://www.example.com"), absl::nullopt, ORIGIN_OMNIBOX, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| |
| EXPECT_TRUE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| GURL("http://www.example.com"), nullptr, gfx::Size())); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| } |
| |
| class PrerenderFallbackToPreconnectDisabledTest : public PrerenderTest { |
| public: |
| PrerenderFallbackToPreconnectDisabledTest() { |
| feature_list_.InitAndDisableFeature( |
| features::kPrerenderFallbackToPreconnect); |
| } |
| }; |
| |
| // Test that when prerender fails and the |
| // kPrerenderFallbackToPreconnect experiment is not enabled, |
| // a prerender initiated by omnibox does not result in a preconnect. |
| TEST_F(PrerenderFallbackToPreconnectDisabledTest, |
| OmniboxAllowedWhenNotDisabled_LowMemory_FeatureDisabled) { |
| const GURL kURL(GURL("http://www.example.com")); |
| predictors::LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| |
| auto* loading_predictor = |
| predictors::LoadingPredictorFactory::GetForProfile(profile()); |
| loading_predictor->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Prerender should be disabled on low memory devices. |
| no_state_prefetch_manager()->SetIsLowEndDevice(true); |
| EXPECT_FALSE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| kURL, nullptr, gfx::Size())); |
| |
| EXPECT_EQ(0u, loading_predictor->GetActiveHintsSizeForTesting()); |
| } |
| |
| class PrerenderFallbackToPreconnectEnabledTest : public PrerenderTest { |
| public: |
| PrerenderFallbackToPreconnectEnabledTest() { |
| feature_list_.InitAndEnableFeature( |
| features::kPrerenderFallbackToPreconnect); |
| } |
| }; |
| |
| // Test that when prerender fails and the |
| // kPrerenderFallbackToPreconnect experiment is enabled, a |
| // prerender initiated by omnibox actually results in preconnect. |
| TEST_F(PrerenderFallbackToPreconnectEnabledTest, |
| Omnibox_AllowedWhenNotDisabled_LowMemory_FeatureEnabled) { |
| const GURL kURL(GURL("http://www.example.com")); |
| |
| predictors::LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| |
| auto* loading_predictor = |
| predictors::LoadingPredictorFactory::GetForProfile(profile()); |
| loading_predictor->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Prerender should be disabled on low memory devices. |
| no_state_prefetch_manager()->SetIsLowEndDevice(true); |
| EXPECT_FALSE(no_state_prefetch_manager()->AddPrerenderFromOmnibox( |
| kURL, nullptr, gfx::Size())); |
| |
| // Verify that the prerender request falls back to a preconnect request. |
| EXPECT_EQ(1u, loading_predictor->GetActiveHintsSizeForTesting()); |
| |
| auto& active_hints = loading_predictor->active_hints_for_testing(); |
| auto it = active_hints.find(kURL); |
| EXPECT_NE(it, active_hints.end()); |
| } |
| |
| // Test that when prerender fails and the |
| // kPrerenderFallbackToPreconnect experiment is enabled, a |
| // prerender initiated by an external request actually results in preconnect. |
| TEST_F(PrerenderFallbackToPreconnectEnabledTest, |
| ExternalRequest_AllowedWhenNotDisabled_LowMemory_FeatureEnabled) { |
| const GURL kURL(GURL("http://www.example.com")); |
| |
| predictors::LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| |
| auto* loading_predictor = |
| predictors::LoadingPredictorFactory::GetForProfile(profile()); |
| loading_predictor->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Prerender should be disabled on low memory devices. |
| no_state_prefetch_manager()->SetIsLowEndDevice(true); |
| EXPECT_FALSE(no_state_prefetch_manager()->AddPrerenderFromExternalRequest( |
| kURL, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize))); |
| |
| // Verify that the prerender request falls back to a preconnect request. |
| EXPECT_EQ(1u, loading_predictor->GetActiveHintsSizeForTesting()); |
| |
| auto& active_hints = loading_predictor->active_hints_for_testing(); |
| auto it = active_hints.find(kURL); |
| EXPECT_NE(it, active_hints.end()); |
| } |
| |
| // Test that when prerender fails and the |
| // kPrerenderFallbackToPreconnect experiment is enabled, a |
| // prerender initiated by isolated prerender does not trigger a preconnect. |
| TEST_F(PrerenderFallbackToPreconnectEnabledTest, |
| IsolatedPrerenderDoesNotPreconnect) { |
| const GURL kURL(GURL("http://www.example.com")); |
| |
| predictors::LoadingPredictorConfig config; |
| PopulateTestConfig(&config); |
| |
| auto* loading_predictor = |
| predictors::LoadingPredictorFactory::GetForProfile(profile()); |
| loading_predictor->StartInitialization(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Prerender should be disabled on low memory devices. |
| no_state_prefetch_manager()->SetIsLowEndDevice(true); |
| EXPECT_FALSE(no_state_prefetch_manager()->AddIsolatedPrerender( |
| kURL, nullptr, kDefaultViewSize)); |
| |
| // Verify that the prerender request does not fall back to a preconnect. |
| EXPECT_EQ(0u, loading_predictor->GetActiveHintsSizeForTesting()); |
| } |
| |
| TEST_F(PrerenderTest, LinkRelStillAllowedWhenDisabled) { |
| DisablePrerender(); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("https://www.notgoogle.com")), |
| ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| TEST_F(PrerenderTest, LinkRelAllowedOnCellular) { |
| EnablePrerender(); |
| GURL url("http://www.example.com"); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifier4GMetered); |
| EXPECT_TRUE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_COST_METERED, |
| net::NetworkChangeNotifier::GetConnectionCost()); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, url::Origin::Create(GURL("https://www.notexample.com")), |
| ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Verify that the external prerender requests are not allowed on cellular |
| // connection when kPredictivePrefetchingAllowedOnAllConnectionTypes feature is |
| // not enabled. |
| TEST_F(PrerenderTest, PrerenderNotAllowedOnCellularWithExternalOrigin) { |
| EnablePrerender(); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifier4GMetered); |
| EXPECT_TRUE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_COST_METERED, |
| net::NetworkChangeNotifier::GetConnectionCost()); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle( |
| no_state_prefetch_manager()->AddPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize))); |
| EXPECT_TRUE(no_state_prefetch_handle); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| histogram_tester().ExpectTotalCount("Prerender.FinalStatus", 0); |
| } |
| |
| // Verify that the external prerender requests are allowed on unmetered cellular |
| // connection when kPredictivePrefetchingAllowedOnAllConnectionTypes feature is |
| // not enabled. |
| TEST_F(PrerenderTest, PrerenderAllowedOnUnmeteredCellularWithExternalOrigin) { |
| EnablePrerender(); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifier4GUnmetered); |
| EXPECT_TRUE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_COST_UNMETERED, |
| net::NetworkChangeNotifier::GetConnectionCost()); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle( |
| no_state_prefetch_manager()->AddPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize))); |
| EXPECT_TRUE(no_state_prefetch_handle); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| histogram_tester().ExpectTotalCount("Prerender.FinalStatus", 0); |
| } |
| |
| // Verify that the external prerender requests are not allowed on metered wifi |
| // connection when kPredictivePrefetchingAllowedOnAllConnectionTypes feature is |
| // not enabled. |
| TEST_F(PrerenderTest, PrerenderNotAllowedOnMeteredWifiWithExternalOrigin) { |
| EnablePrerender(); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifierWifiMetered); |
| EXPECT_FALSE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_COST_METERED, |
| net::NetworkChangeNotifier::GetConnectionCost()); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle( |
| no_state_prefetch_manager()->AddPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize))); |
| EXPECT_TRUE(no_state_prefetch_handle); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| histogram_tester().ExpectTotalCount("Prerender.FinalStatus", 0); |
| } |
| |
| class PrerenderPrefetchingAllowedOnAllTest : public PrerenderTest { |
| public: |
| PrerenderPrefetchingAllowedOnAllTest() { |
| feature_list_.InitAndEnableFeature( |
| features::kPredictivePrefetchingAllowedOnAllConnectionTypes); |
| } |
| }; |
| |
| // Verify that the external prerender requests are allowed on cellular |
| // connection when kPredictivePrefetchingAllowedOnAllConnectionTypes feature is |
| // enabled. |
| TEST_F( |
| PrerenderPrefetchingAllowedOnAllTest, |
| PrerenderAllowedOnCellularWithExternalOrigin_PredictivePrefetchingAllowedOnAllConnectionTypes) { |
| EnablePrerender(); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifier4GMetered); |
| EXPECT_TRUE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST, FINAL_STATUS_USED); |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle( |
| no_state_prefetch_manager()->AddPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize))); |
| EXPECT_TRUE(no_state_prefetch_handle); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_EQ(no_state_prefetch_contents, no_state_prefetch_handle->contents()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| TEST_F(PrerenderTest, PrerenderAllowedForForcedCellular) { |
| EnablePrerender(); |
| std::unique_ptr<net::NetworkChangeNotifier> mock( |
| new MockNetworkChangeNotifier4GMetered); |
| EXPECT_TRUE(net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType())); |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = nullptr; |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle; |
| no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, |
| FINAL_STATUS_USED); |
| no_state_prefetch_handle = |
| no_state_prefetch_manager()->AddForcedPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize)); |
| EXPECT_TRUE(no_state_prefetch_handle); |
| EXPECT_TRUE(no_state_prefetch_handle->IsPrefetching()); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_EQ(no_state_prefetch_contents, no_state_prefetch_handle->contents()); |
| EXPECT_EQ(ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, |
| no_state_prefetch_handle->contents()->origin()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| TEST_F(PrerenderTest, LinkManagerCancel) { |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| CancelLastTrigger(); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| } |
| |
| TEST_F(PrerenderTest, LinkManagerAbandon) { |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| AbandonLastTrigger(); |
| |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| } |
| |
| TEST_F(PrerenderTest, LinkManagerAbandonThenCancel) { |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| AbandonLastTrigger(); |
| |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| |
| CancelLastTrigger(); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // Flaky on Android, crbug.com/1087876. |
| // Flaky on Mac and Linux, crbug.com/1087735. |
| #if defined(OS_ANDROID) || defined(OS_MAC) || defined(OS_LINUX) || \ |
| defined(OS_CHROMEOS) |
| #define MAYBE_LinkManagerAddTwiceCancelTwice \ |
| DISABLED_LinkManagerAddTwiceCancelTwice |
| #else |
| #define MAYBE_LinkManagerAddTwiceCancelTwice LinkManagerAddTwiceCancelTwice |
| #endif |
| TEST_F(PrerenderTest, MAYBE_LinkManagerAddTwiceCancelTwice) { |
| SetConcurrency(2); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| CancelFirstTrigger(); |
| |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| CancelFirstTrigger(); |
| |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // TODO(gavinp): Update this test after abandon has an effect on Prerenders, |
| // like shortening the timeouts. |
| // Flaky on Android and Linux, crbug.com/1087876 & crbug.com/1087736. |
| TEST_F(PrerenderTest, DISABLED_LinkManagerAddTwiceAbandonTwiceUseTwice) { |
| SetConcurrency(2); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| AbandonFirstTrigger(); |
| |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| AbandonFirstTrigger(); |
| |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(no_state_prefetch_contents, entry.get()); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| } |
| |
| // TODO(gavinp): After abandon shortens the expire time on a Prerender, |
| // add a series of tests testing advancing the time by either the abandon |
| // or normal expire, and verifying the expected behaviour with groups |
| // of links. |
| TEST_F(PrerenderTest, LinkManagerExpireThenCancel) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_TIMED_OUT); |
| |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| |
| EXPECT_TRUE(no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE(no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| tick_clock()->Advance(no_state_prefetch_manager()->config().time_to_live + |
| TimeDelta::FromSeconds(1)); |
| |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| |
| // FindEntry will have a side-effect of pruning expired prerenders. |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| TEST_F(PrerenderTest, LinkManagerExpireThenAddAgain) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* first_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(first_no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE( |
| first_no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(first_no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| tick_clock()->Advance(no_state_prefetch_manager()->config().time_to_live + |
| TimeDelta::FromSeconds(1)); |
| |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| DummyNoStatePrefetchContents* second_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(second_no_state_prefetch_contents->prerendering_has_started()); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(url); |
| ASSERT_EQ(second_no_state_prefetch_contents, entry.get()); |
| } |
| |
| // Flaky on Android, crbug.com/1087876. |
| TEST_F(PrerenderTest, DISABLED_LinkManagerCancelThenAddAgain) { |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| GURL url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* first_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_CANCELLED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(url)); |
| EXPECT_TRUE(first_no_state_prefetch_contents->prerendering_has_started()); |
| EXPECT_FALSE( |
| first_no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_EQ(first_no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(url)); |
| CancelLastTrigger(); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| EXPECT_TRUE( |
| first_no_state_prefetch_contents->prerendering_has_been_cancelled()); |
| ASSERT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| |
| // A cancelled NoStatePrefetch is counted as a prefetch recently happened. A |
| // new attempt to prefetch should return as duplicate. |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, FINAL_STATUS_PROFILE_DESTROYED); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(url)); |
| } |
| |
| // Creates two prerenders, one of which should be blocked by the |
| // max_link_concurrency; abandons both of them and waits to make sure both |
| // are cleared from the NoStatePrefetchLinkManager. |
| TEST_F(PrerenderTest, DISABLED_LinkManagerAbandonInactivePrerender) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| SetConcurrency(1); |
| ASSERT_LT(no_state_prefetch_manager()->config().abandon_time_to_live, |
| no_state_prefetch_manager()->config().time_to_live); |
| GURL first_url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| first_url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(first_url)); |
| |
| GURL second_url("http://www.neverlaunched.com"); |
| EXPECT_FALSE(AddSimpleLinkTrigger(second_url)); |
| |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| |
| AbandonFirstTrigger(); |
| AbandonFirstTrigger(); |
| |
| tick_clock()->Advance( |
| no_state_prefetch_manager()->config().abandon_time_to_live + |
| TimeDelta::FromSeconds(1)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| } |
| |
| // Creates two prerenders, one of which should be blocked by the |
| // max_link_concurrency; uses one after the max wait to launch, and |
| // ensures the second prerender does not start. |
| TEST_F(PrerenderTest, LinkManagerWaitToLaunchNotLaunched) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| SetConcurrency(1); |
| ASSERT_LT(no_state_prefetch_manager()->config().max_wait_to_launch, |
| no_state_prefetch_manager()->config().time_to_live); |
| GURL first_url("http://www.myexample.com"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| first_url, FINAL_STATUS_USED); |
| EXPECT_TRUE(AddSimpleLinkTrigger(first_url)); |
| |
| GURL second_url("http://www.neverlaunched.com"); |
| EXPECT_FALSE(AddSimpleLinkTrigger(second_url)); |
| |
| EXPECT_FALSE(IsEmptyNoStatePrefetchLinkManager()); |
| |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| |
| tick_clock()->Advance( |
| no_state_prefetch_manager()->config().max_wait_to_launch + |
| TimeDelta::FromSeconds(1)); |
| EXPECT_EQ(no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(first_url); |
| EXPECT_EQ(no_state_prefetch_contents, entry.get()); |
| |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| EXPECT_TRUE(IsEmptyNoStatePrefetchLinkManager()); |
| } |
| |
| // Creates two prerenders, one of which should start when the first one expires. |
| TEST_F(PrerenderTest, LinkManagerExpireRevealingLaunch) { |
| no_state_prefetch_manager()->SetTickClockForTesting(tick_clock()); |
| SetConcurrency(1); |
| ASSERT_LT(no_state_prefetch_manager()->config().max_wait_to_launch, |
| no_state_prefetch_manager()->config().time_to_live); |
| |
| GURL first_url("http://www.willexpire.com"); |
| DummyNoStatePrefetchContents* first_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| first_url, FINAL_STATUS_TIMED_OUT); |
| EXPECT_TRUE(AddSimpleLinkTrigger(first_url)); |
| EXPECT_EQ(first_no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(first_url)); |
| |
| // Insert the second prerender so it will be still be launchable when the |
| // first expires. |
| const TimeDelta wait_to_launch_second_prerender = |
| no_state_prefetch_manager()->config().time_to_live - |
| no_state_prefetch_manager()->config().max_wait_to_launch + |
| TimeDelta::FromSeconds(2); |
| const TimeDelta wait_for_first_prerender_to_expire = |
| no_state_prefetch_manager()->config().time_to_live - |
| wait_to_launch_second_prerender + TimeDelta::FromSeconds(1); |
| ASSERT_LT( |
| no_state_prefetch_manager()->config().time_to_live, |
| wait_to_launch_second_prerender + wait_for_first_prerender_to_expire); |
| ASSERT_GT( |
| no_state_prefetch_manager()->config().max_wait_to_launch.InSeconds(), |
| wait_for_first_prerender_to_expire.InSeconds()); |
| |
| tick_clock()->Advance(wait_to_launch_second_prerender); |
| GURL second_url("http://www.willlaunch.com"); |
| DummyNoStatePrefetchContents* second_no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| second_url, FINAL_STATUS_USED); |
| EXPECT_FALSE(AddSimpleLinkTrigger(second_url)); |
| |
| // The first prerender is still running, but the second has not yet launched. |
| EXPECT_EQ(first_no_state_prefetch_contents, |
| no_state_prefetch_manager()->FindEntry(first_url)); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(second_url)); |
| |
| // The first prerender should have died, giving life to the second one. |
| tick_clock()->Advance(wait_for_first_prerender_to_expire); |
| EXPECT_FALSE(no_state_prefetch_manager()->FindEntry(first_url)); |
| std::unique_ptr<NoStatePrefetchContents> entry = |
| no_state_prefetch_manager()->FindAndUseEntry(second_url); |
| EXPECT_EQ(second_no_state_prefetch_contents, entry.get()); |
| } |
| |
| TEST_F(PrerenderTest, NoStatePrefetchContentsIsValidHttpMethod) { |
| EXPECT_TRUE(IsValidHttpMethod("GET")); |
| EXPECT_TRUE(IsValidHttpMethod("HEAD")); |
| EXPECT_FALSE(IsValidHttpMethod("OPTIONS")); |
| EXPECT_FALSE(IsValidHttpMethod("POST")); |
| EXPECT_FALSE(IsValidHttpMethod("TRACE")); |
| EXPECT_FALSE(IsValidHttpMethod("WHATEVER")); |
| } |
| |
| TEST_F(PrerenderTest, NoStatePrefetchContentsIncrementsByteCount) { |
| GURL url("http://www.google.com/"); |
| DummyNoStatePrefetchContents* no_state_prefetch_contents = |
| no_state_prefetch_manager()->CreateNextNoStatePrefetchContents( |
| url, absl::nullopt, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, |
| FINAL_STATUS_PROFILE_DESTROYED); |
| std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle = |
| no_state_prefetch_manager()->AddForcedPrerenderFromExternalRequest( |
| url, content::Referrer(), nullptr, gfx::Rect(kDefaultViewSize)); |
| |
| TestNetworkBytesChangedObserver observer; |
| no_state_prefetch_handle->SetObserver(&observer); |
| |
| no_state_prefetch_contents->AddNetworkBytes(12); |
| EXPECT_TRUE(observer.network_bytes_changed()); |
| EXPECT_EQ(12, no_state_prefetch_contents->network_bytes()); |
| } |
| |
| TEST_F(PrerenderTest, NoPrerenderInSingleProcess) { |
| GURL url("http://www.google.com/"); |
| auto* command_line = base::CommandLine::ForCurrentProcess(); |
| ASSERT_TRUE(command_line != nullptr); |
| command_line->AppendSwitch(switches::kSingleProcess); |
| EXPECT_FALSE(AddSimpleLinkTrigger(url)); |
| histogram_tester().ExpectUniqueSample("Prerender.FinalStatus", |
| FINAL_STATUS_SINGLE_PROCESS, 1); |
| } |
| |
| } // namespace prerender |