Home | History | Annotate | Download | only in search
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/search/search_tab_helper.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/browser/search/search.h"
     11 #include "chrome/browser/search_engines/template_url_service.h"
     12 #include "chrome/browser/search_engines/template_url_service_factory.h"
     13 #include "chrome/browser/signin/fake_signin_manager.h"
     14 #include "chrome/browser/signin/signin_manager_factory.h"
     15 #include "chrome/browser/sync/profile_sync_service.h"
     16 #include "chrome/browser/sync/profile_sync_service_factory.h"
     17 #include "chrome/browser/sync/profile_sync_service_mock.h"
     18 #include "chrome/browser/ui/search/search_ipc_router.h"
     19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     20 #include "chrome/common/chrome_switches.h"
     21 #include "chrome/common/ntp_logging_events.h"
     22 #include "chrome/common/omnibox_focus_state.h"
     23 #include "chrome/common/render_messages.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "chrome/test/base/browser_with_test_window_test.h"
     26 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     27 #include "chrome/test/base/testing_profile.h"
     28 #include "chrome/test/base/ui_test_utils.h"
     29 #include "content/public/browser/navigation_controller.h"
     30 #include "content/public/browser/navigation_entry.h"
     31 #include "content/public/browser/web_contents.h"
     32 #include "content/public/test/mock_render_process_host.h"
     33 #include "grit/generated_resources.h"
     34 #include "ipc/ipc_message.h"
     35 #include "ipc/ipc_test_sink.h"
     36 #include "net/base/net_errors.h"
     37 #include "testing/gmock/include/gmock/gmock.h"
     38 #include "testing/gtest/include/gtest/gtest.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "url/gurl.h"
     41 
     42 using testing::Return;
     43 
     44 namespace {
     45 
     46 class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
     47  public:
     48   virtual ~MockSearchIPCRouterDelegate() {}
     49 
     50   MOCK_METHOD1(OnInstantSupportDetermined, void(bool supports_instant));
     51   MOCK_METHOD1(OnSetVoiceSearchSupport, void(bool supports_voice_search));
     52   MOCK_METHOD1(FocusOmnibox, void(OmniboxFocusState state));
     53   MOCK_METHOD3(NavigateToURL, void(const GURL&, WindowOpenDisposition, bool));
     54   MOCK_METHOD1(OnDeleteMostVisitedItem, void(const GURL& url));
     55   MOCK_METHOD1(OnUndoMostVisitedDeletion, void(const GURL& url));
     56   MOCK_METHOD0(OnUndoAllMostVisitedDeletions, void());
     57   MOCK_METHOD1(OnLogEvent, void(NTPLoggingEventType event));
     58   MOCK_METHOD2(OnLogMostVisitedImpression,
     59                void(int position, const base::string16& provider));
     60   MOCK_METHOD2(OnLogMostVisitedNavigation,
     61                void(int position, const base::string16& provider));
     62   MOCK_METHOD1(PasteIntoOmnibox, void(const base::string16&));
     63   MOCK_METHOD1(OnChromeIdentityCheck, void(const base::string16& identity));
     64 };
     65 
     66 }  // namespace
     67 
     68 class SearchTabHelperTest : public ChromeRenderViewHostTestHarness {
     69  public:
     70   virtual void SetUp() {
     71     ChromeRenderViewHostTestHarness::SetUp();
     72     SearchTabHelper::CreateForWebContents(web_contents());
     73   }
     74 
     75   virtual content::BrowserContext* CreateBrowserContext() OVERRIDE {
     76     TestingProfile::Builder builder;
     77     builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
     78                               FakeSigninManagerBase::Build);
     79     builder.AddTestingFactory(
     80         ProfileSyncServiceFactory::GetInstance(),
     81         ProfileSyncServiceMock::BuildMockProfileSyncService);
     82     return builder.Build().release();
     83   }
     84 
     85   // Creates a sign-in manager for tests.  If |username| is not empty, the
     86   // testing profile of the WebContents will be connected to the given account.
     87   // The account can be configured to |sync_history| or not.
     88   void CreateSigninManager(const std::string& username, bool sync_history) {
     89     SigninManagerBase* signin_manager = static_cast<SigninManagerBase*>(
     90         SigninManagerFactory::GetForProfile(profile()));
     91 
     92     if (!username.empty()) {
     93       ASSERT_TRUE(signin_manager);
     94       signin_manager->SetAuthenticatedUsername(username);
     95     }
     96 
     97     ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
     98         ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
     99 
    100     EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true));
    101     syncer::ModelTypeSet result;
    102     if (sync_history) {
    103       result.Put(syncer::HISTORY_DELETE_DIRECTIVES);
    104     }
    105     EXPECT_CALL(*sync_service, GetActiveDataTypes())
    106         .WillRepeatedly(Return(result));
    107   }
    108 
    109   bool MessageWasSent(uint32 id) {
    110     return process()->sink().GetFirstMessageMatching(id) != NULL;
    111   }
    112 
    113   MockSearchIPCRouterDelegate* mock_delegate() { return &delegate_; }
    114 
    115  private:
    116   MockSearchIPCRouterDelegate delegate_;
    117 };
    118 
    119 TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_Local) {
    120   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    121   EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(0);
    122 
    123   SearchTabHelper* search_tab_helper =
    124       SearchTabHelper::FromWebContents(web_contents());
    125   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    126   search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
    127   search_tab_helper->DetermineIfPageSupportsInstant();
    128 }
    129 
    130 TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_NonLocal) {
    131   NavigateAndCommit(GURL("chrome-search://foo/bar"));
    132   process()->sink().ClearMessages();
    133   EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(1);
    134 
    135   SearchTabHelper* search_tab_helper =
    136       SearchTabHelper::FromWebContents(web_contents());
    137   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    138   search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
    139   search_tab_helper->DetermineIfPageSupportsInstant();
    140   ASSERT_TRUE(MessageWasSent(ChromeViewMsg_DetermineIfPageSupportsInstant::ID));
    141 
    142   scoped_ptr<IPC::Message> response(
    143       new ChromeViewHostMsg_InstantSupportDetermined(
    144           web_contents()->GetRoutingID(),
    145           search_tab_helper->ipc_router().page_seq_no_for_testing(),
    146           true));
    147   search_tab_helper->ipc_router().OnMessageReceived(*response);
    148 }
    149 
    150 TEST_F(SearchTabHelperTest, PageURLDoesntBelongToInstantRenderer) {
    151   // Navigate to a page URL that doesn't belong to Instant renderer.
    152   // SearchTabHelper::DeterminerIfPageSupportsInstant() should return
    153   // immediately without dispatching any message to the renderer.
    154   NavigateAndCommit(GURL("http://www.example.com"));
    155   process()->sink().ClearMessages();
    156   EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(false)).Times(0);
    157 
    158   SearchTabHelper* search_tab_helper =
    159       SearchTabHelper::FromWebContents(web_contents());
    160   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    161   search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate());
    162   search_tab_helper->DetermineIfPageSupportsInstant();
    163   ASSERT_FALSE(MessageWasSent(
    164       ChromeViewMsg_DetermineIfPageSupportsInstant::ID));
    165 }
    166 
    167 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatch) {
    168   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    169   CreateSigninManager(std::string("foo (at) bar.com"), true);
    170   SearchTabHelper* search_tab_helper =
    171       SearchTabHelper::FromWebContents(web_contents());
    172   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    173 
    174   const base::string16 test_identity = base::ASCIIToUTF16("foo (at) bar.com");
    175   search_tab_helper->OnChromeIdentityCheck(test_identity);
    176 
    177   const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
    178       ChromeViewMsg_ChromeIdentityCheckResult::ID);
    179   ASSERT_TRUE(message != NULL);
    180 
    181   ChromeViewMsg_ChromeIdentityCheckResult::Param params;
    182   ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
    183   EXPECT_EQ(test_identity, params.a);
    184   ASSERT_TRUE(params.b);
    185 }
    186 
    187 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMismatch) {
    188   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    189   CreateSigninManager(std::string("foo (at) bar.com"), true);
    190   SearchTabHelper* search_tab_helper =
    191       SearchTabHelper::FromWebContents(web_contents());
    192   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    193 
    194   const base::string16 test_identity = base::ASCIIToUTF16("bar (at) foo.com");
    195   search_tab_helper->OnChromeIdentityCheck(test_identity);
    196 
    197   const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
    198       ChromeViewMsg_ChromeIdentityCheckResult::ID);
    199   ASSERT_TRUE(message != NULL);
    200 
    201   ChromeViewMsg_ChromeIdentityCheckResult::Param params;
    202   ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
    203   EXPECT_EQ(test_identity, params.a);
    204   ASSERT_FALSE(params.b);
    205 }
    206 
    207 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckSignedOutMatch) {
    208   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    209   // This test does not sign in.
    210   ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
    211       ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
    212   EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(false));
    213   SearchTabHelper* search_tab_helper =
    214       SearchTabHelper::FromWebContents(web_contents());
    215   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    216 
    217   const base::string16 test_identity;
    218   search_tab_helper->OnChromeIdentityCheck(test_identity);
    219 
    220   const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
    221       ChromeViewMsg_ChromeIdentityCheckResult::ID);
    222   ASSERT_TRUE(message != NULL);
    223 
    224   ChromeViewMsg_ChromeIdentityCheckResult::Param params;
    225   ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
    226   EXPECT_EQ(test_identity, params.a);
    227   ASSERT_FALSE(params.b);
    228 }
    229 
    230 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckSignedOutMismatch) {
    231   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    232   // This test does not sign in.
    233   ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
    234       ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
    235   EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(false));
    236   SearchTabHelper* search_tab_helper =
    237       SearchTabHelper::FromWebContents(web_contents());
    238   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    239 
    240   const base::string16 test_identity = base::ASCIIToUTF16("bar (at) foo.com");
    241   search_tab_helper->OnChromeIdentityCheck(test_identity);
    242 
    243   const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
    244       ChromeViewMsg_ChromeIdentityCheckResult::ID);
    245   ASSERT_TRUE(message != NULL);
    246 
    247   ChromeViewMsg_ChromeIdentityCheckResult::Param params;
    248   ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
    249   EXPECT_EQ(test_identity, params.a);
    250   ASSERT_FALSE(params.b);
    251 }
    252 
    253 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatchNotSyncing) {
    254   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
    255   CreateSigninManager(std::string("foo (at) bar.com"), false);
    256   SearchTabHelper* search_tab_helper =
    257       SearchTabHelper::FromWebContents(web_contents());
    258   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    259 
    260   const base::string16 test_identity = base::ASCIIToUTF16("foo (at) bar.com");
    261   search_tab_helper->OnChromeIdentityCheck(test_identity);
    262 
    263   const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
    264       ChromeViewMsg_ChromeIdentityCheckResult::ID);
    265   ASSERT_TRUE(message != NULL);
    266 
    267   ChromeViewMsg_ChromeIdentityCheckResult::Param params;
    268   ChromeViewMsg_ChromeIdentityCheckResult::Read(message, &params);
    269   EXPECT_EQ(test_identity, params.a);
    270   ASSERT_FALSE(params.b);
    271 }
    272 
    273 class TabTitleObserver : public content::WebContentsObserver {
    274  public:
    275   explicit TabTitleObserver(content::WebContents* contents)
    276       : WebContentsObserver(contents) {}
    277 
    278   base::string16 title_on_start() { return title_on_start_; }
    279   base::string16 title_on_commit() { return title_on_commit_; }
    280 
    281  private:
    282   virtual void DidStartProvisionalLoadForFrame(
    283       int64 /* frame_id */,
    284       int64 /* parent_frame_id */,
    285       bool /* is_main_frame */,
    286       const GURL& /* validated_url */,
    287       bool /* is_error_page */,
    288       bool /* is_iframe_srcdoc */,
    289       content::RenderViewHost* /* render_view_host */) OVERRIDE {
    290     title_on_start_ = web_contents()->GetTitle();
    291   }
    292 
    293   virtual void DidNavigateMainFrame(
    294       const content::LoadCommittedDetails& /* details */,
    295       const content::FrameNavigateParams& /* params */) OVERRIDE {
    296     title_on_commit_ = web_contents()->GetTitle();
    297   }
    298 
    299   base::string16 title_on_start_;
    300   base::string16 title_on_commit_;
    301 };
    302 
    303 TEST_F(SearchTabHelperTest, TitleIsSetForNTP) {
    304   TabTitleObserver title_observer(web_contents());
    305   NavigateAndCommit(GURL(chrome::kChromeUINewTabURL));
    306   const base::string16 title = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
    307   EXPECT_EQ(title, title_observer.title_on_start());
    308   EXPECT_EQ(title, title_observer.title_on_commit());
    309   EXPECT_EQ(title, web_contents()->GetTitle());
    310 }
    311 
    312 class SearchTabHelperWindowTest : public BrowserWithTestWindowTest {
    313  protected:
    314   virtual void SetUp() OVERRIDE {
    315     BrowserWithTestWindowTest::SetUp();
    316     TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    317         profile(), &TemplateURLServiceFactory::BuildInstanceFor);
    318     TemplateURLService* template_url_service =
    319         TemplateURLServiceFactory::GetForProfile(profile());
    320     ui_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
    321 
    322     TemplateURLData data;
    323     data.SetURL("http://foo.com/url?bar={searchTerms}");
    324     data.instant_url = "http://foo.com/instant?"
    325         "{google:omniboxStartMarginParameter}{google:forceInstantResults}"
    326         "foo=foo#foo=foo&strk";
    327     data.new_tab_url = std::string("https://foo.com/newtab?strk");
    328     data.alternate_urls.push_back("http://foo.com/alt#quux={searchTerms}");
    329     data.search_terms_replacement_key = "strk";
    330 
    331     TemplateURL* template_url = new TemplateURL(data);
    332     template_url_service->Add(template_url);
    333     template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
    334   }
    335 };
    336 
    337 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailRedirectNTPToLocal) {
    338   AddTab(browser(), GURL(chrome::kChromeUINewTabURL));
    339   content::WebContents* contents =
    340         browser()->tab_strip_model()->GetWebContentsAt(0);
    341   content::NavigationController* controller = &contents->GetController();
    342 
    343   SearchTabHelper* search_tab_helper =
    344       SearchTabHelper::FromWebContents(contents);
    345   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    346 
    347   // A failed provisional load of a cacheable NTP should be redirected to local
    348   // NTP.
    349   const GURL cacheableNTPURL = chrome::GetNewTabPageURL(profile());
    350   search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true,
    351       cacheableNTPURL, 1, base::string16(), NULL);
    352   CommitPendingLoad(controller);
    353   EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
    354                  controller->GetLastCommittedEntry()->GetURL());
    355 }
    356 
    357 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailDontRedirectIfAborted) {
    358   AddTab(browser(), GURL("chrome://blank"));
    359   content::WebContents* contents =
    360         browser()->tab_strip_model()->GetWebContentsAt(0);
    361   content::NavigationController* controller = &contents->GetController();
    362 
    363   SearchTabHelper* search_tab_helper =
    364       SearchTabHelper::FromWebContents(contents);
    365   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    366 
    367   // A failed provisional load of a cacheable NTP should be redirected to local
    368   // NTP.
    369   const GURL cacheableNTPURL = chrome::GetNewTabPageURL(profile());
    370   search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true,
    371       cacheableNTPURL, net::ERR_ABORTED, base::string16(), NULL);
    372   CommitPendingLoad(controller);
    373   EXPECT_EQ(GURL("chrome://blank"),
    374                  controller->GetLastCommittedEntry()->GetURL());
    375 }
    376 
    377 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailDontRedirectNonNTP) {
    378   AddTab(browser(), GURL(chrome::kChromeUINewTabURL));
    379   content::WebContents* contents =
    380         browser()->tab_strip_model()->GetWebContentsAt(0);
    381   content::NavigationController* controller = &contents->GetController();
    382 
    383   SearchTabHelper* search_tab_helper =
    384       SearchTabHelper::FromWebContents(contents);
    385   ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
    386 
    387   // Any other web page shouldn't be redirected when provisional load fails.
    388   search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true,
    389       GURL("http://www.example.com"), 1, base::string16(), NULL);
    390   CommitPendingLoad(controller);
    391   EXPECT_NE(GURL(chrome::kChromeSearchLocalNtpUrl),
    392                  controller->GetLastCommittedEntry()->GetURL());
    393 }
    394