blob: cdbb03c765bfeadfb3135f4eb309abc0bc10e9e0 [file] [log] [blame]
// Copyright 2018 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 "base/macros.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/navigation_predictor/navigation_predictor.h"
#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prerender/browser/prerender_manager.h"
#include "components/search_engines/template_url_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
namespace {
// Verifies that all URLs specified in |expected_urls| are present in
// |urls_from_observed_prediction|. Ordering of URLs is NOT verified.
void VerifyURLsPresent(const std::vector<GURL>& urls_from_observed_prediction,
const std::vector<std::string>& expected_urls) {
for (const auto& expected_url : expected_urls) {
EXPECT_NE(urls_from_observed_prediction.end(),
std::find(urls_from_observed_prediction.begin(),
urls_from_observed_prediction.end(), expected_url));
}
}
class NavigationPredictorBrowserTest
: public subresource_filter::SubresourceFilterBrowserTest,
public testing::WithParamInterface<bool> {
public:
NavigationPredictorBrowserTest()
: subresource_filter::SubresourceFilterBrowserTest() {
feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kNavigationPredictor, {});
}
void SetUp() override {
https_server_.reset(
new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
https_server_->ServeFilesFromSourceDirectory(
"chrome/test/data/navigation_predictor");
ASSERT_TRUE(https_server_->Start());
http_server_.reset(
new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTP));
http_server_->ServeFilesFromSourceDirectory(
"chrome/test/data/navigation_predictor");
ASSERT_TRUE(http_server_->Start());
subresource_filter::SubresourceFilterBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
subresource_filter::SubresourceFilterBrowserTest::SetUpOnMainThread();
host_resolver()->ClearRules();
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
const GURL GetTestURL(const char* file) const {
return https_server_->GetURL(file);
}
const GURL GetHttpTestURL(const char* file) const {
return http_server_->GetURL(file);
}
void WaitForLayout() {
const char* entry_name =
ukm::builders::NavigationPredictorPageLinkMetrics::kEntryName;
if (ukm_recorder_->GetEntriesByName(entry_name).empty()) {
base::RunLoop run_loop;
ukm_recorder_->SetOnAddEntryCallback(entry_name, run_loop.QuitClosure());
run_loop.Run();
}
}
void ResetUKM() {
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
private:
std::unique_ptr<net::EmbeddedTestServer> http_server_;
std::unique_ptr<net::EmbeddedTestServer> https_server_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(NavigationPredictorBrowserTest);
};
class TestObserver : public NavigationPredictorKeyedService::Observer {
public:
TestObserver() {}
~TestObserver() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
base::Optional<NavigationPredictorKeyedService::Prediction> last_prediction()
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return last_prediction_;
}
size_t count_predictions() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return count_predictions_;
}
// Waits until the count if received notifications is at least
// |expected_notifications_count|.
void WaitUntilNotificationsCountReached(size_t expected_notifications_count) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Ensure that |wait_loop_| is null implying there is no ongoing wait.
ASSERT_FALSE(wait_loop_);
if (count_predictions_ >= expected_notifications_count)
return;
expected_notifications_count_ = expected_notifications_count;
wait_loop_ = std::make_unique<base::RunLoop>();
wait_loop_->Run();
wait_loop_.reset();
}
private:
void OnPredictionUpdated(
const base::Optional<NavigationPredictorKeyedService::Prediction>
prediction) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++count_predictions_;
last_prediction_ = prediction;
if (wait_loop_ && count_predictions_ >= expected_notifications_count_) {
wait_loop_->Quit();
}
}
// Count of prediction notifications received so far.
size_t count_predictions_ = 0u;
// last prediction received.
base::Optional<NavigationPredictorKeyedService::Prediction> last_prediction_;
// If |wait_loop_| is non-null, then it quits as soon as count of received
// notifications are at least |expected_notifications_count_|.
std::unique_ptr<base::RunLoop> wait_loop_;
base::Optional<size_t> expected_notifications_count_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(TestObserver);
};
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, Pipeline) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
}
// Test that no metrics are recorded in off-the-record profiles.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, PipelineOffTheRecord) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
Browser* incognito = CreateIncognitoBrowser();
ui_test_utils::NavigateToURL(incognito, url);
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectTotalCount(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 0);
}
// Test that the browser does not process anchor element metrics from an http
// web page on page load.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, PipelineHttp) {
base::HistogramTester histogram_tester;
const GURL& url = GetHttpTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectTotalCount(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 0);
}
// Simulate a click at the anchor element.
// Test that timing info (DurationLoadToFirstClick) can be recorded.
// And that the navigation score can be looked up.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, ClickAnchorElement) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kNone, 1);
}
// Simulate a click at the anchor element.
// Test that the action accuracy is properly recorded.
// User clicks on an anchor element that points to a origin different than the
// origin of the URL prefetched.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ActionAccuracy_DifferentOrigin) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.LinkClickedPrerenderResult",
NavigationPredictor::PrerenderResult::kCrossOriginAboveThreshold, 1);
}
// Disabled because it fails when SingleProcessMash feature is enabled. Since
// Navigation Predictor is not going to be enabled on Chrome OS, disabling the
// browser test on that platform is fine.
#if defined(OS_CHROMEOS)
#define DISABLE_ON_CHROMEOS(x) DISABLED_##x
#else
#define DISABLE_ON_CHROMEOS(x) x
#endif
// Simulate a click at the anchor element.
// Test that the action accuracy is properly recorded.
// User clicks on an anchor element that points to a origin different than the
// origin of the URL prefetched.
IN_PROC_BROWSER_TEST_F(
NavigationPredictorBrowserTest,
DISABLE_ON_CHROMEOS(ActionAccuracy_DifferentOrigin_VisibilityChanged)) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
base::RunLoop().RunUntilIdle();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.LinkClickedPrerenderResult",
NavigationPredictor::PrerenderResult::kCrossOriginAboveThreshold, 1);
}
class NavigationPredictorBrowserTestWithDefaultPredictorEnabled
: public NavigationPredictorBrowserTest {
public:
NavigationPredictorBrowserTestWithDefaultPredictorEnabled() {
feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kNavigationPredictor, {});
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Test that the action accuracy is properly recorded and when same origin
// preconnections are enabled, then navigation predictor initiates the
// preconnection.
IN_PROC_BROWSER_TEST_F(
NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
DISABLE_ON_CHROMEOS(
ActionAccuracy_DifferentOrigin_VisibilityChangedPreconnectEnabled)) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
}
IN_PROC_BROWSER_TEST_F(
NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
DISABLE_ON_CHROMEOS(PreconnectNonSearch)) {
base::HistogramTester histogram_tester;
// This page only has non-same host links.
const GURL& url = GetTestURL("/anchors_different_area.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kNone, 1);
}
class NavigationPredictorBrowserTestWithPrefetchAfterPreconnect
: public NavigationPredictorBrowserTest {
public:
NavigationPredictorBrowserTestWithPrefetchAfterPreconnect()
: NavigationPredictorBrowserTest() {
feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kNavigationPredictor,
{{"prefetch_after_preconnect", "true"}});
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(
NavigationPredictorBrowserTestWithPrefetchAfterPreconnect,
DISABLE_ON_CHROMEOS(PrefetchAfterPreconnect)) {
prerender::PrerenderManager::SetMode(
prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder =
std::make_unique<ukm::TestAutoSetUkmRecorder>();
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('example').click();"));
EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents()));
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
histogram_tester.ExpectTotalCount(
"NavigationPredictor.LinkClickedPrerenderResult", 1);
const auto& entries = ukm_recorder->GetMergedEntriesByName(
ukm::builders::NoStatePrefetch::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
EXPECT_TRUE(ukm_recorder->EntryHasMetric(
kv.second.get(),
ukm::builders::NoStatePrefetch::kPrefetchedRecently_FinalStatusName));
EXPECT_TRUE(ukm_recorder->EntryHasMetric(
kv.second.get(),
ukm::builders::NoStatePrefetch::kPrefetchedRecently_OriginName));
EXPECT_TRUE(ukm_recorder->EntryHasMetric(
kv.second.get(),
ukm::builders::NoStatePrefetch::kPrefetchedRecently_PrefetchAgeName));
}
}
// Simulate a click at the anchor element.
// Test that the action accuracy is properly recorded.
// User clicks on an anchor element that points to same URL as the URL
// prefetched.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ActionAccuracy_SameOrigin) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('example').click();"));
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectTotalCount(
"NavigationPredictor.LinkClickedPrerenderResult", 1);
}
// Simulate a click at the anchor element in off-the-record profile.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ClickAnchorElementOffTheRecord) {
auto test_ukm_recorder = std::make_unique<ukm::TestAutoSetUkmRecorder>();
ResetUKM();
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
Browser* incognito = CreateIncognitoBrowser();
ui_test_utils::NavigateToURL(incognito, url);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(content::ExecuteScript(
incognito->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
content::WaitForLoadStop(
incognito->tab_strip_model()->GetActiveWebContents());
auto entries = test_ukm_recorder->GetMergedEntriesByName(
ukm::builders::PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
}
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
AnchorElementClickedOnSearchEnginePage) {
static const char kShortName[] = "test";
static const char kSearchURL[] = "/anchors_same_href.html?q={searchTerms}";
TemplateURLService* model =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
ASSERT_TRUE(model);
search_test_utils::WaitForTemplateURLServiceToLoad(model);
ASSERT_TRUE(model->loaded());
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16(kShortName));
data.SetKeyword(data.short_name());
data.SetURL(GetTestURL(kSearchURL).spec());
TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
ASSERT_TRUE(template_url);
model->SetUserSelectedDefaultSearchProvider(template_url);
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/anchors_same_href.html?q=cats");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
base::RunLoop().RunUntilIdle();
// Anchor element with id 'google' points to an href that's on a different
// host.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Clicked.OnDSE.SameHost", 0, 1);
}
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
AnchorElementClickedOnNonSearchEnginePage) {
static const char kShortName[] = "test";
static const char kSearchURL[] = "/somne_other_url.html?q={searchTerms}";
TemplateURLService* model =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
ASSERT_TRUE(model);
search_test_utils::WaitForTemplateURLServiceToLoad(model);
ASSERT_TRUE(model->loaded());
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16(kShortName));
data.SetKeyword(data.short_name());
data.SetURL(GetTestURL(kSearchURL).spec());
TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
ASSERT_TRUE(template_url);
model->SetUserSelectedDefaultSearchProvider(template_url);
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/anchors_same_href.html?q=cats");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById('google').click();"));
base::RunLoop().RunUntilIdle();
// Anchor element with id 'google' points to an href that's on a different
// host.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Clicked.OnNonDSE.SameHost", 0, 1);
}
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ActionPrefetch_NoSameHostAnchorElement) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kNone, 1);
}
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ActionPrefetch_SameHostAnchorElement) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Same document anchor element should be removed after merge.
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.OnNonDSE.ActionTaken",
NavigationPredictor::Action::kPrefetch, 1);
}
// Tests that the browser receives anchors from anywhere on the page.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
ViewportOnlyAndUrlIncrementByOne) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/long_page_with_anchors-1.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
histogram_tester.ExpectUniqueSample(
"AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 3, 1);
}
// Test that navigation score of anchor elements can be calculated on page load
// and the predicted URLs for the next navigation are dispatched to the single
// observer.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
NavigationScoreSingleObserver) {
TestObserver observer;
NavigationPredictorKeyedService* service =
NavigationPredictorKeyedServiceFactory::GetForProfile(
browser()->profile());
EXPECT_NE(nullptr, service);
service->AddObserver(&observer);
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
observer.WaitUntilNotificationsCountReached(1);
service->RemoveObserver(&observer);
EXPECT_EQ(1u, observer.count_predictions());
EXPECT_EQ(url, observer.last_prediction()->source_document_url());
EXPECT_THAT(observer.last_prediction()->sorted_predicted_urls(),
::testing::UnorderedElementsAre("https://google.com/",
"https://example.com/"));
// Doing another navigation after removing the observer should not cause a
// crash.
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
EXPECT_EQ(1u, observer.count_predictions());
}
// Same as NavigationScoreSingleObserver test but with more than one observer.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
NavigationScore_TwoObservers) {
TestObserver observer_1;
TestObserver observer_2;
NavigationPredictorKeyedService* service =
NavigationPredictorKeyedServiceFactory::GetForProfile(
browser()->profile());
service->AddObserver(&observer_1);
service->AddObserver(&observer_2);
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/simple_page_with_anchors.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
observer_1.WaitUntilNotificationsCountReached(1);
observer_2.WaitUntilNotificationsCountReached(1);
service->RemoveObserver(&observer_1);
EXPECT_EQ(1u, observer_1.count_predictions());
EXPECT_EQ(url, observer_1.last_prediction()->source_document_url());
EXPECT_EQ(2u, observer_1.last_prediction()->sorted_predicted_urls().size());
VerifyURLsPresent(observer_1.last_prediction()->sorted_predicted_urls(),
{"https://google.com/", "https://example.com/"});
EXPECT_EQ(1u, observer_2.count_predictions());
EXPECT_EQ(url, observer_2.last_prediction()->source_document_url());
// Only |observer_2| should get the notification since |observer_1| has
// been removed from receiving the notifications.
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
observer_2.WaitUntilNotificationsCountReached(2);
EXPECT_EQ(1u, observer_1.count_predictions());
EXPECT_EQ(2u, observer_2.count_predictions());
VerifyURLsPresent(observer_2.last_prediction()->sorted_predicted_urls(),
{"https://google.com/", "https://example.com/"});
}
// Test that the navigation predictor keyed service is null for incognito
// profiles.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest, Incognito) {
Browser* incognito = CreateIncognitoBrowser();
NavigationPredictorKeyedService* incognito_service =
NavigationPredictorKeyedServiceFactory::GetForProfile(
incognito->profile());
EXPECT_EQ(nullptr, incognito_service);
}
// Verify that the observers are notified of predictions on search results page.
IN_PROC_BROWSER_TEST_F(
NavigationPredictorBrowserTestWithPrefetchAfterPreconnect,
DISABLE_ON_CHROMEOS(ObserverNotifiedOnSearchPage)) {
TestObserver observer;
NavigationPredictorKeyedService* service =
NavigationPredictorKeyedServiceFactory::GetForProfile(
browser()->profile());
service->AddObserver(&observer);
static const char kShortName[] = "test";
static const char kSearchURL[] =
"/anchors_different_area.html?q={searchTerms}";
// Set up default search engine.
TemplateURLService* model =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
ASSERT_TRUE(model);
search_test_utils::WaitForTemplateURLServiceToLoad(model);
ASSERT_TRUE(model->loaded());
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16(kShortName));
data.SetKeyword(data.short_name());
data.SetURL(GetTestURL(kSearchURL).spec());
TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
ASSERT_TRUE(template_url);
model->SetUserSelectedDefaultSearchProvider(template_url);
base::HistogramTester histogram_tester;
EXPECT_EQ(0u, observer.count_predictions());
// This page only has non-same host links.
const GURL& url = GetTestURL("/anchors_different_area.html?q=cats");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
observer.WaitUntilNotificationsCountReached(1u);
histogram_tester.ExpectUniqueSample("NavigationPredictor.OnDSE.ActionTaken",
NavigationPredictor::Action::kNone, 1);
EXPECT_EQ(1u, observer.count_predictions());
EXPECT_EQ(url, observer.last_prediction()->source_document_url());
EXPECT_EQ(5u, observer.last_prediction()->sorted_predicted_urls().size());
VerifyURLsPresent(
observer.last_prediction()->sorted_predicted_urls(),
{"https://example.com/2", "https://google.com/", "https://example.com/1",
"https://example.com/", "https://dummy.com/"});
}
const base::Feature kNavigationPredictorMultiplePrerenders{
"NavigationPredictorMultiplePrerenders", base::FEATURE_ENABLED_BY_DEFAULT};
class NavigationPredictorBrowserTestMultiplePrerender
: public NavigationPredictorBrowserTest {
public:
NavigationPredictorBrowserTestMultiplePrerender() {
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kNavigationPredictor,
{{"prefetch_after_preconnect", "true"}}},
{kNavigationPredictorMultiplePrerenders, {{"prerender_limit", "4"}}}},
{});
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Test that multiple prerenders is working.
IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTestMultiplePrerender,
DISABLE_ON_CHROMEOS(MultiplePrerendersRecordsMetrics)) {
base::HistogramTester histogram_tester;
const GURL& url = GetTestURL("/anchors_large.html");
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Force prerenders to happen quickly.
browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
// Force recording stats.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfURLsAboveThreshold", 5, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfURLsAboveThreshold.CrossOrigin", 2, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfURLsAboveThreshold.SameOrigin", 3, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfURLsInPredictedSet.CrossOrigin", 1, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfURLsInPredictedSet.SameOrigin", 3, 1);
histogram_tester.ExpectUniqueSample(
"NavigationPredictor.CountOfStartedPrerenders", 3, 1);
// Same origin links in anchors_large.html
std::vector<GURL> prerendered_urls = {
GetTestURL("/1.html"), GetTestURL("/2.html"), GetTestURL("/3.html")};
for (auto& url : prerendered_urls) {
auto test_ukm_recorder = std::make_unique<ukm::TestAutoSetUkmRecorder>();
ResetUKM();
// Load page from cache.
ui_test_utils::NavigateToURL(browser(), url);
WaitForLayout();
// Force recording PageLoad UKM.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
// Check that the page was loaded from cache.
auto entries = test_ukm_recorder->GetMergedEntriesByName(
ukm::builders::PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
auto* const cached_load_entry = kv.second.get();
test_ukm_recorder->ExpectEntrySourceHasUrl(cached_load_entry, url);
EXPECT_TRUE(test_ukm_recorder->EntryHasMetric(
cached_load_entry, ukm::builders::PageLoad::kWasCachedName));
}
}
}
} // namespace