|  | // Copyright 2020 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 "components/omnibox/browser/most_visited_sites_provider.h" | 
|  |  | 
|  | #include <list> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  |  | 
|  | #include "build/build_config.h" | 
|  | #include "components/history/core/browser/top_sites.h" | 
|  | #include "components/omnibox/browser/autocomplete_provider_listener.h" | 
|  | #include "components/omnibox/browser/mock_autocomplete_provider_client.h" | 
|  | #include "components/omnibox/browser/omnibox_field_trial.h" | 
|  | #include "components/omnibox/browser/test_scheme_classifier.h" | 
|  | #include "components/omnibox/common/omnibox_features.h" | 
|  | #include "components/search_engines/omnibox_focus_type.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "third_party/metrics_proto/omnibox_event.pb.h" | 
|  |  | 
|  | namespace { | 
|  | class FakeEmptyTopSites : public history::TopSites { | 
|  | public: | 
|  | // history::TopSites: | 
|  | void GetMostVisitedURLs(GetMostVisitedURLsCallback callback) override; | 
|  | void SyncWithHistory() override {} | 
|  | bool HasBlockedUrls() const override { return false; } | 
|  | void AddBlockedUrl(const GURL& url) override {} | 
|  | void RemoveBlockedUrl(const GURL& url) override {} | 
|  | bool IsBlocked(const GURL& url) override { return false; } | 
|  | void ClearBlockedUrls() override {} | 
|  | bool IsFull() override { return false; } | 
|  | bool loaded() const override { return false; } | 
|  | history::PrepopulatedPageList GetPrepopulatedPages() override { | 
|  | return history::PrepopulatedPageList(); | 
|  | } | 
|  | void OnNavigationCommitted(const GURL& url) override {} | 
|  |  | 
|  | // RefcountedKeyedService: | 
|  | void ShutdownOnUIThread() override {} | 
|  |  | 
|  | // Only runs a single callback, so that the test can specify a different | 
|  | // set per call. | 
|  | void RunACallback(const history::MostVisitedURLList& urls) { | 
|  | DCHECK(!callbacks.empty()); | 
|  | std::move(callbacks.front()).Run(urls); | 
|  | callbacks.pop_front(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // A test-specific field for controlling when most visited callback is run | 
|  | // after top sites have been requested. | 
|  | std::list<GetMostVisitedURLsCallback> callbacks; | 
|  |  | 
|  | ~FakeEmptyTopSites() override = default; | 
|  | }; | 
|  |  | 
|  | void FakeEmptyTopSites::GetMostVisitedURLs( | 
|  | GetMostVisitedURLsCallback callback) { | 
|  | callbacks.push_back(std::move(callback)); | 
|  | } | 
|  |  | 
|  | class FakeAutocompleteProviderClient : public MockAutocompleteProviderClient { | 
|  | public: | 
|  | FakeAutocompleteProviderClient() | 
|  | : template_url_service_(new TemplateURLService(nullptr, 0)), | 
|  | top_sites_(new FakeEmptyTopSites()) {} | 
|  | FakeAutocompleteProviderClient(const FakeAutocompleteProviderClient&) = | 
|  | delete; | 
|  | FakeAutocompleteProviderClient& operator=( | 
|  | const FakeAutocompleteProviderClient&) = delete; | 
|  |  | 
|  | bool SearchSuggestEnabled() const override { return true; } | 
|  |  | 
|  | scoped_refptr<history::TopSites> GetTopSites() override { return top_sites_; } | 
|  |  | 
|  | TemplateURLService* GetTemplateURLService() override { | 
|  | return template_url_service_.get(); | 
|  | } | 
|  |  | 
|  | TemplateURLService* GetTemplateURLService() const override { | 
|  | return template_url_service_.get(); | 
|  | } | 
|  |  | 
|  | bool IsPersonalizedUrlDataCollectionActive() const override { return true; } | 
|  |  | 
|  | void Classify( | 
|  | const std::u16string& text, | 
|  | bool prefer_keyword, | 
|  | bool allow_exact_keyword_match, | 
|  | metrics::OmniboxEventProto::PageClassification page_classification, | 
|  | AutocompleteMatch* match, | 
|  | GURL* alternate_nav_url) override { | 
|  | // Populate enough of |match| to keep the MostVisitedSitesProvider happy. | 
|  | match->type = AutocompleteMatchType::URL_WHAT_YOU_TYPED; | 
|  | match->destination_url = GURL(text); | 
|  | } | 
|  |  | 
|  | const AutocompleteSchemeClassifier& GetSchemeClassifier() const override { | 
|  | return scheme_classifier_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<TemplateURLService> template_url_service_; | 
|  | scoped_refptr<history::TopSites> top_sites_; | 
|  | TestSchemeClassifier scheme_classifier_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class MostVisitedSitesProviderTest : public testing::Test, | 
|  | public AutocompleteProviderListener { | 
|  | public: | 
|  | MostVisitedSitesProviderTest() = default; | 
|  | MostVisitedSitesProviderTest(const MostVisitedSitesProviderTest&) = delete; | 
|  | MostVisitedSitesProviderTest& operator=(const MostVisitedSitesProviderTest&) = | 
|  | delete; | 
|  |  | 
|  | void SetUp() override; | 
|  |  | 
|  | protected: | 
|  | // AutocompleteProviderListener: | 
|  | void OnProviderUpdate(bool updated_matches) override; | 
|  |  | 
|  | std::unique_ptr<FakeAutocompleteProviderClient> client_; | 
|  | scoped_refptr<MostVisitedSitesProvider> provider_; | 
|  |  | 
|  | network::TestURLLoaderFactory* test_loader_factory() { | 
|  | return client_->test_url_loader_factory(); | 
|  | } | 
|  |  | 
|  | GURL GetSuggestURL( | 
|  | metrics::OmniboxEventProto::PageClassification page_classification) { | 
|  | TemplateURLRef::SearchTermsArgs search_terms_args; | 
|  | search_terms_args.page_classification = page_classification; | 
|  | search_terms_args.focus_type = OmniboxFocusType::ON_FOCUS; | 
|  | return RemoteSuggestionsService::EndpointUrl( | 
|  | search_terms_args, client_->GetTemplateURLService()); | 
|  | } | 
|  |  | 
|  | AutocompleteInput CreateNTPOnFocusInputForRemoteNoUrl() { | 
|  | // Use NTP as the page classification, since REMOTE_NO_URL is enabled by | 
|  | // default for the NTP. | 
|  | AutocompleteInput input( | 
|  | std::u16string(), | 
|  | metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, | 
|  | TestSchemeClassifier()); | 
|  | input.set_focus_type(OmniboxFocusType::ON_FOCUS); | 
|  | return input; | 
|  | } | 
|  | }; | 
|  |  | 
|  | void MostVisitedSitesProviderTest::SetUp() { | 
|  | client_ = std::make_unique<FakeAutocompleteProviderClient>(); | 
|  | provider_ = new MostVisitedSitesProvider(client_.get(), this); | 
|  | } | 
|  |  | 
|  | void MostVisitedSitesProviderTest::OnProviderUpdate(bool updated_matches) {} | 
|  |  | 
|  | TEST_F(MostVisitedSitesProviderTest, AllowMostVisitedSitesSuggestions) { | 
|  | std::string input_url = "https://example.com/"; | 
|  |  | 
|  | AutocompleteInput prefix_input(base::ASCIIToUTF16(input_url), | 
|  | metrics::OmniboxEventProto::OTHER, | 
|  | TestSchemeClassifier()); | 
|  | prefix_input.set_focus_type(OmniboxFocusType::DEFAULT); | 
|  |  | 
|  | AutocompleteInput on_focus_input(base::ASCIIToUTF16(input_url), | 
|  | metrics::OmniboxEventProto::OTHER, | 
|  | TestSchemeClassifier()); | 
|  | on_focus_input.set_current_url(GURL(input_url)); | 
|  | on_focus_input.set_focus_type(OmniboxFocusType::ON_FOCUS); | 
|  |  | 
|  | AutocompleteInput on_clobber_input(std::u16string(), | 
|  | metrics::OmniboxEventProto::OTHER, | 
|  | TestSchemeClassifier()); | 
|  | on_clobber_input.set_current_url(GURL(input_url)); | 
|  | on_clobber_input.set_focus_type(OmniboxFocusType::DELETED_PERMANENT_TEXT); | 
|  |  | 
|  | // MostVisited should never deal with prefix suggestions. | 
|  | EXPECT_FALSE(provider_->AllowMostVisitedSitesSuggestions(prefix_input)); | 
|  |  | 
|  | // This should always be true, as otherwise we will break MostVisited. | 
|  | EXPECT_TRUE(provider_->AllowMostVisitedSitesSuggestions(on_focus_input)); | 
|  | } | 
|  |  | 
|  | TEST_F(MostVisitedSitesProviderTest, TestMostVisitedCallback) { | 
|  | std::string current_url("http://www.foxnews.com/"); | 
|  | std::string input_url("http://www.cnn.com/"); | 
|  | AutocompleteInput input(base::ASCIIToUTF16(input_url), | 
|  | metrics::OmniboxEventProto::OTHER, | 
|  | TestSchemeClassifier()); | 
|  | input.set_current_url(GURL(current_url)); | 
|  | input.set_focus_type(OmniboxFocusType::ON_FOCUS); | 
|  | history::MostVisitedURLList urls; | 
|  | history::MostVisitedURL url(GURL("http://foo.com/"), u"Foo"); | 
|  | urls.push_back(url); | 
|  |  | 
|  | provider_->Start(input, false); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | scoped_refptr<history::TopSites> top_sites = client_->GetTopSites(); | 
|  | static_cast<FakeEmptyTopSites*>(top_sites.get())->RunACallback(urls); | 
|  | EXPECT_EQ(1U, provider_->matches().size()); | 
|  | provider_->Stop(false, false); | 
|  |  | 
|  | provider_->Start(input, false); | 
|  | provider_->Stop(false, false); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | // Most visited results arriving after Stop() has been called, ensure they | 
|  | // are not displayed. | 
|  | static_cast<FakeEmptyTopSites*>(top_sites.get())->RunACallback(urls); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  |  | 
|  | history::MostVisitedURLList urls2; | 
|  | urls2.push_back(history::MostVisitedURL(GURL("http://bar.com/"), u"Bar")); | 
|  | urls2.push_back(history::MostVisitedURL(GURL("http://zinga.com/"), u"Zinga")); | 
|  | provider_->Start(input, false); | 
|  | provider_->Stop(false, false); | 
|  | provider_->Start(input, false); | 
|  | static_cast<FakeEmptyTopSites*>(top_sites.get())->RunACallback(urls); | 
|  | // Stale results should get rejected. | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | static_cast<FakeEmptyTopSites*>(top_sites.get())->RunACallback(urls2); | 
|  | EXPECT_FALSE(provider_->matches().empty()); | 
|  | provider_->Stop(false, false); | 
|  | } | 
|  |  | 
|  | TEST_F(MostVisitedSitesProviderTest, TestMostVisitedNavigateToSearchPage) { | 
|  | std::string current_url("http://www.foxnews.com/"); | 
|  | std::string input_url("http://www.cnn.com/"); | 
|  | AutocompleteInput input(base::ASCIIToUTF16(input_url), | 
|  | metrics::OmniboxEventProto::OTHER, | 
|  | TestSchemeClassifier()); | 
|  | input.set_current_url(GURL(current_url)); | 
|  | input.set_focus_type(OmniboxFocusType::ON_FOCUS); | 
|  | history::MostVisitedURLList urls; | 
|  | history::MostVisitedURL url(GURL("http://foo.com/"), u"Foo"); | 
|  | urls.push_back(url); | 
|  |  | 
|  | provider_->Start(input, false); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | // Stop() doesn't always get called. | 
|  |  | 
|  | std::string search_url("https://www.google.com/?q=flowers"); | 
|  | AutocompleteInput srp_input( | 
|  | base::ASCIIToUTF16(search_url), | 
|  | metrics::OmniboxEventProto::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT, | 
|  | TestSchemeClassifier()); | 
|  | srp_input.set_current_url(GURL(search_url)); | 
|  | srp_input.set_focus_type(OmniboxFocusType::ON_FOCUS); | 
|  |  | 
|  | provider_->Start(srp_input, false); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | // Most visited results arriving after a new request has been started. | 
|  | scoped_refptr<history::TopSites> top_sites = client_->GetTopSites(); | 
|  | static_cast<FakeEmptyTopSites*>(top_sites.get())->RunACallback(urls); | 
|  | EXPECT_TRUE(provider_->matches().empty()); | 
|  | } |