blob: a16c3ac9ec8adb8c51d28257a1e261d0dae6508b [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/ui/search/search_tab_helper.h"
#include <stdint.h>
#include <string>
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/search/instant_unittest_base.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/fake_signin_manager_builder.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/ui/search/search_ipc_router.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/ntp_logging_events.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/browser_sync/browser/profile_sync_service.h"
#include "components/omnibox/common/omnibox_focus_state.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_test_sink.h"
#include "net/base/net_errors.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
class OmniboxView;
using testing::Return;
namespace {
class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
public:
virtual ~MockSearchIPCRouterDelegate() {}
MOCK_METHOD1(OnInstantSupportDetermined, void(bool supports_instant));
MOCK_METHOD1(FocusOmnibox, void(OmniboxFocusState state));
MOCK_METHOD3(NavigateToURL, void(const GURL&, WindowOpenDisposition, bool));
MOCK_METHOD1(OnDeleteMostVisitedItem, void(const GURL& url));
MOCK_METHOD1(OnUndoMostVisitedDeletion, void(const GURL& url));
MOCK_METHOD0(OnUndoAllMostVisitedDeletions, void());
MOCK_METHOD2(OnLogEvent, void(NTPLoggingEventType event,
base::TimeDelta time));
MOCK_METHOD2(OnLogMostVisitedImpression,
void(int position, const base::string16& provider));
MOCK_METHOD2(OnLogMostVisitedNavigation,
void(int position, const base::string16& provider));
MOCK_METHOD1(PasteIntoOmnibox, void(const base::string16&));
MOCK_METHOD1(OnChromeIdentityCheck, void(const base::string16& identity));
MOCK_METHOD0(OnHistorySyncCheck, void());
};
} // namespace
class SearchTabHelperTest : public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
SearchTabHelper::CreateForWebContents(web_contents());
}
content::BrowserContext* CreateBrowserContext() override {
TestingProfile::Builder builder;
builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
BuildFakeSigninManagerBase);
builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(),
BuildMockProfileSyncService);
return builder.Build().release();
}
// Creates a sign-in manager for tests. If |username| is not empty, the
// testing profile of the WebContents will be connected to the given account.
void CreateSigninManager(const std::string& username) {
SigninManagerBase* signin_manager = static_cast<SigninManagerBase*>(
SigninManagerFactory::GetForProfile(profile()));
if (!username.empty()) {
ASSERT_TRUE(signin_manager);
signin_manager->SetAuthenticatedAccountInfo(username, username);
}
}
// Configure the account to |sync_history| or not.
void SetHistorySync(bool sync_history) {
ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
syncer::ModelTypeSet result;
if (sync_history) {
result.Put(syncer::HISTORY_DELETE_DIRECTIVES);
}
EXPECT_CALL(*sync_service, GetPreferredDataTypes())
.WillRepeatedly(Return(result));
}
bool MessageWasSent(uint32_t id) {
return process()->sink().GetFirstMessageMatching(id) != NULL;
}
MockSearchIPCRouterDelegate* mock_delegate() { return &delegate_; }
private:
MockSearchIPCRouterDelegate delegate_;
};
TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_Local) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(0);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
search_tab_helper->DetermineIfPageSupportsInstant();
}
TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_NonLocal) {
NavigateAndCommit(GURL("chrome-search://foo/bar"));
process()->sink().ClearMessages();
EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(1);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
search_tab_helper->DetermineIfPageSupportsInstant();
ASSERT_TRUE(MessageWasSent(ChromeViewMsg_DetermineIfPageSupportsInstant::ID));
scoped_ptr<IPC::Message> response(
new ChromeViewHostMsg_InstantSupportDetermined(
web_contents()->GetRoutingID(),
search_tab_helper->ipc_router().page_seq_no_for_testing(),
true));
search_tab_helper->ipc_router().OnMessageReceived(*response);
}
TEST_F(SearchTabHelperTest, PageURLDoesntBelongToInstantRenderer) {
// Navigate to a page URL that doesn't belong to Instant renderer.
// SearchTabHelper::DeterminerIfPageSupportsInstant() should return
// immediately without dispatching any message to the renderer.
NavigateAndCommit(GURL("http://www.example.com"));
process()->sink().ClearMessages();
EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(false)).Times(0);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
search_tab_helper->DetermineIfPageSupportsInstant();
ASSERT_FALSE(MessageWasSent(
ChromeViewMsg_DetermineIfPageSupportsInstant::ID));
}
TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatch) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
CreateSigninManager(std::string("foo@bar.com"));
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
const base::string16 test_identity = base::ASCIIToUTF16("foo@bar.com");
search_tab_helper->OnChromeIdentityCheck(test_identity);
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_ChromeIdentityCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_ChromeIdentityCheckResult::Param params;
ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
EXPECT_EQ(test_identity, base::get<0>(params));
ASSERT_TRUE(base::get<1>(params));
}
TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatchSlightlyDifferentGmail) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
CreateSigninManager(std::string("foobar123@gmail.com"));
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
// For gmail, canonicalization is done so that email addresses have a
// standard form.
const base::string16 test_identity =
base::ASCIIToUTF16("Foo.Bar.123@gmail.com");
search_tab_helper->OnChromeIdentityCheck(test_identity);
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_ChromeIdentityCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_ChromeIdentityCheckResult::Param params;
ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
EXPECT_EQ(test_identity, base::get<0>(params));
ASSERT_TRUE(base::get<1>(params));
}
TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatchSlightlyDifferentGmail2) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
//
CreateSigninManager(std::string("chrome.guy.7FOREVER"));
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
// For gmail/googlemail, canonicalization is done so that email addresses have
// a standard form.
const base::string16 test_identity =
base::ASCIIToUTF16("chromeguy7forever@googlemail.com");
search_tab_helper->OnChromeIdentityCheck(test_identity);
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_ChromeIdentityCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_ChromeIdentityCheckResult::Param params;
ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
EXPECT_EQ(test_identity, base::get<0>(params));
ASSERT_TRUE(base::get<1>(params));
}
TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMismatch) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
CreateSigninManager(std::string("foo@bar.com"));
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
const base::string16 test_identity = base::ASCIIToUTF16("bar@foo.com");
search_tab_helper->OnChromeIdentityCheck(test_identity);
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_ChromeIdentityCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_ChromeIdentityCheckResult::Param params;
ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
EXPECT_EQ(test_identity, base::get<0>(params));
ASSERT_FALSE(base::get<1>(params));
}
TEST_F(SearchTabHelperTest, OnChromeIdentityCheckSignedOutMismatch) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
// This test does not sign in.
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
const base::string16 test_identity = base::ASCIIToUTF16("bar@foo.com");
search_tab_helper->OnChromeIdentityCheck(test_identity);
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_ChromeIdentityCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_ChromeIdentityCheckResult::Param params;
ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
EXPECT_EQ(test_identity, base::get<0>(params));
ASSERT_FALSE(base::get<1>(params));
}
TEST_F(SearchTabHelperTest, OnHistorySyncCheckSyncing) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
SetHistorySync(true);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
search_tab_helper->OnHistorySyncCheck();
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_HistorySyncCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_HistorySyncCheckResult::Param params;
ChromeViewMsg_HistorySyncCheckResult::Read(message, &params);
ASSERT_TRUE(base::get<0>(params));
}
TEST_F(SearchTabHelperTest, OnHistorySyncCheckNotSyncing) {
NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
SetHistorySync(false);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
search_tab_helper->OnHistorySyncCheck();
const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
ChromeViewMsg_HistorySyncCheckResult::ID);
ASSERT_TRUE(message != NULL);
ChromeViewMsg_HistorySyncCheckResult::Param params;
ChromeViewMsg_HistorySyncCheckResult::Read(message, &params);
ASSERT_FALSE(base::get<0>(params));
}
TEST_F(SearchTabHelperTest, OnMostVisitedItemsChangedFromServer) {
InstantMostVisitedItem item;
item.is_server_side_suggestion = true;
std::vector<InstantMostVisitedItem> items;
items.push_back(item);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
auto logger = NTPUserDataLogger::GetOrCreateFromWebContents(web_contents());
ASSERT_FALSE(logger->has_server_side_suggestions_);
ASSERT_FALSE(logger->has_client_side_suggestions_);
search_tab_helper->MostVisitedItemsChanged(items);
ASSERT_TRUE(logger->has_server_side_suggestions_);
ASSERT_FALSE(logger->has_client_side_suggestions_);
}
TEST_F(SearchTabHelperTest, OnMostVisitedItemsChangedFromClient) {
InstantMostVisitedItem item;
item.is_server_side_suggestion = false;
std::vector<InstantMostVisitedItem> items;
items.push_back(item);
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
auto logger = NTPUserDataLogger::GetOrCreateFromWebContents(web_contents());
ASSERT_FALSE(logger->has_server_side_suggestions_);
ASSERT_FALSE(logger->has_client_side_suggestions_);
search_tab_helper->MostVisitedItemsChanged(items);
ASSERT_FALSE(logger->has_server_side_suggestions_);
ASSERT_TRUE(logger->has_client_side_suggestions_);
}
class TabTitleObserver : public content::WebContentsObserver {
public:
explicit TabTitleObserver(content::WebContents* contents)
: WebContentsObserver(contents) {}
base::string16 title_on_start() { return title_on_start_; }
base::string16 title_on_commit() { return title_on_commit_; }
private:
void DidStartProvisionalLoadForFrame(
content::RenderFrameHost* /* render_frame_host */,
const GURL& /* validated_url */,
bool /* is_error_page */,
bool /* is_iframe_srcdoc */) override {
title_on_start_ = web_contents()->GetTitle();
}
void DidNavigateMainFrame(
const content::LoadCommittedDetails& /* details */,
const content::FrameNavigateParams& /* params */) override {
title_on_commit_ = web_contents()->GetTitle();
}
base::string16 title_on_start_;
base::string16 title_on_commit_;
};
TEST_F(SearchTabHelperTest, TitleIsSetForNTP) {
TabTitleObserver title_observer(web_contents());
NavigateAndCommit(GURL(chrome::kChromeUINewTabURL));
const base::string16 title = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
EXPECT_EQ(title, title_observer.title_on_start());
EXPECT_EQ(title, title_observer.title_on_commit());
EXPECT_EQ(title, web_contents()->GetTitle());
}
class SearchTabHelperPrerenderTest : public InstantUnitTestBase {
public:
~SearchTabHelperPrerenderTest() override {}
protected:
void SetUp() override {
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"EmbeddedSearch",
"Group1 espv:89 prefetch_results:1 "
"prerender_instant_url_on_omnibox_focus:1"));
InstantUnitTestBase::SetUp();
AddTab(browser(), GURL(chrome::kChromeUINewTabURL));
SearchTabHelper::FromWebContents(web_contents())->set_omnibox_has_focus_fn(
omnibox_has_focus);
SearchTabHelperPrerenderTest::omnibox_has_focus_ = true;
}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
bool IsInstantURLMarkedForPrerendering() {
GURL instant_url(search::GetSearchResultPrefetchBaseURL(profile()));
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForProfile(profile());
return prerender_manager->HasPrerenderedUrl(instant_url, web_contents());
}
static bool omnibox_has_focus(OmniboxView* omnibox) {
return omnibox_has_focus_;
}
static bool omnibox_has_focus_;
};
bool SearchTabHelperPrerenderTest::omnibox_has_focus_ = true;
TEST_F(SearchTabHelperPrerenderTest, OnOmniboxFocusPrerenderInstantURL) {
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
search_tab_helper->OmniboxFocusChanged(OMNIBOX_FOCUS_VISIBLE,
OMNIBOX_FOCUS_CHANGE_EXPLICIT);
ASSERT_TRUE(IsInstantURLMarkedForPrerendering());
search_tab_helper->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE,
OMNIBOX_FOCUS_CHANGE_EXPLICIT);
ASSERT_FALSE(IsInstantURLMarkedForPrerendering());
}
TEST_F(SearchTabHelperPrerenderTest, OnTabActivatedPrerenderInstantURL) {
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
search_tab_helper->OnTabActivated();
ASSERT_TRUE(IsInstantURLMarkedForPrerendering());
}
TEST_F(SearchTabHelperPrerenderTest,
OnTabActivatedNoPrerenderIfOmniboxBlurred) {
SearchTabHelperPrerenderTest::omnibox_has_focus_ = false;
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(web_contents());
search_tab_helper->OnTabActivated();
ASSERT_FALSE(IsInstantURLMarkedForPrerendering());
}