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/instant_page.h" 6 7 #include "base/command_line.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "chrome/browser/ui/search/search_tab_helper.h" 10 #include "chrome/common/chrome_switches.h" 11 #include "chrome/common/render_messages.h" 12 #include "chrome/common/url_constants.h" 13 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 14 #include "content/public/browser/navigation_controller.h" 15 #include "content/public/browser/navigation_entry.h" 16 #include "content/public/browser/web_contents.h" 17 #include "content/public/test/mock_render_process_host.h" 18 #include "ipc/ipc_test_sink.h" 19 #include "testing/gmock/include/gmock/gmock.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "url/gurl.h" 22 23 class Profile; 24 25 namespace { 26 27 class FakePageDelegate : public InstantPage::Delegate { 28 public: 29 virtual ~FakePageDelegate() { 30 } 31 32 MOCK_METHOD2(InstantSupportDetermined, 33 void(const content::WebContents* contents, 34 bool supports_instant)); 35 MOCK_METHOD1(InstantPageRenderProcessGone, 36 void(const content::WebContents* contents)); 37 MOCK_METHOD2(InstantPageAboutToNavigateMainFrame, 38 void(const content::WebContents* contents, 39 const GURL& url)); 40 MOCK_METHOD2(FocusOmnibox, 41 void(const content::WebContents* contents, 42 OmniboxFocusState state)); 43 MOCK_METHOD5(NavigateToURL, 44 void(const content::WebContents* contents, 45 const GURL& url, 46 content::PageTransition transition, 47 WindowOpenDisposition disposition, 48 bool is_search_type)); 49 MOCK_METHOD2(PasteIntoOmnibox, 50 void(const content::WebContents* contents, 51 const string16& text)); 52 MOCK_METHOD1(DeleteMostVisitedItem, void(const GURL& url)); 53 MOCK_METHOD1(UndoMostVisitedDeletion, void(const GURL& url)); 54 MOCK_METHOD0(UndoAllMostVisitedDeletions, void()); 55 MOCK_METHOD1(InstantPageLoadFailed, void(content::WebContents* contents)); 56 }; 57 58 class FakePage : public InstantPage { 59 public: 60 FakePage(Delegate* delegate, const std::string& instant_url, 61 Profile* profile, bool is_incognito); 62 virtual ~FakePage(); 63 64 // InstantPage overrride. 65 virtual bool ShouldProcessDeleteMostVisitedItem() OVERRIDE; 66 virtual bool ShouldProcessUndoMostVisitedDeletion() OVERRIDE; 67 virtual bool ShouldProcessUndoAllMostVisitedDeletions() OVERRIDE; 68 69 void set_should_handle_messages(bool should_handle_messages); 70 71 using InstantPage::SetContents; 72 73 private: 74 // Initialized to true to handle the messages sent by the renderer. 75 bool should_handle_messages_; 76 77 DISALLOW_COPY_AND_ASSIGN(FakePage); 78 }; 79 80 FakePage::FakePage(Delegate* delegate, const std::string& instant_url, 81 Profile* profile, bool is_incognito) 82 : InstantPage(delegate, instant_url, profile, is_incognito), 83 should_handle_messages_(true) { 84 } 85 86 FakePage::~FakePage() { 87 } 88 89 bool FakePage::ShouldProcessDeleteMostVisitedItem() { 90 return should_handle_messages_; 91 } 92 93 bool FakePage::ShouldProcessUndoMostVisitedDeletion() { 94 return should_handle_messages_; 95 } 96 97 bool FakePage::ShouldProcessUndoAllMostVisitedDeletions() { 98 return should_handle_messages_; 99 } 100 101 void FakePage::set_should_handle_messages(bool should_handle_messages) { 102 should_handle_messages_ = should_handle_messages; 103 } 104 105 } // namespace 106 107 class InstantPageTest : public ChromeRenderViewHostTestHarness { 108 public: 109 virtual void SetUp() OVERRIDE; 110 111 bool MessageWasSent(uint32 id) { 112 return process()->sink().GetFirstMessageMatching(id) != NULL; 113 } 114 115 scoped_ptr<FakePage> page; 116 FakePageDelegate delegate; 117 }; 118 119 void InstantPageTest::SetUp() { 120 CommandLine::ForCurrentProcess()->AppendSwitch( 121 switches::kEnableInstantExtendedAPI); 122 ChromeRenderViewHostTestHarness::SetUp(); 123 SearchTabHelper::CreateForWebContents(web_contents()); 124 } 125 126 TEST_F(InstantPageTest, IsLocal) { 127 page.reset(new FakePage(&delegate, "", NULL, false)); 128 EXPECT_FALSE(page->supports_instant()); 129 EXPECT_FALSE(page->IsLocal()); 130 page->SetContents(web_contents()); 131 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 132 EXPECT_TRUE(page->IsLocal()); 133 NavigateAndCommit(GURL("http://example.com")); 134 EXPECT_FALSE(page->IsLocal()); 135 } 136 137 TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_Local) { 138 page.reset(new FakePage(&delegate, "", NULL, false)); 139 EXPECT_FALSE(page->supports_instant()); 140 page->SetContents(web_contents()); 141 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 142 EXPECT_TRUE(page->IsLocal()); 143 EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true)) 144 .Times(1); 145 SearchTabHelper::FromWebContents(web_contents())-> 146 DetermineIfPageSupportsInstant(); 147 EXPECT_TRUE(page->supports_instant()); 148 } 149 150 TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_NonLocal) { 151 page.reset(new FakePage(&delegate, "", NULL, false)); 152 EXPECT_FALSE(page->supports_instant()); 153 page->SetContents(web_contents()); 154 NavigateAndCommit(GURL("chrome-search://foo/bar")); 155 EXPECT_FALSE(page->IsLocal()); 156 process()->sink().ClearMessages(); 157 SearchTabHelper::FromWebContents(web_contents())-> 158 DetermineIfPageSupportsInstant(); 159 const IPC::Message* message = process()->sink().GetFirstMessageMatching( 160 ChromeViewMsg_DetermineIfPageSupportsInstant::ID); 161 ASSERT_TRUE(message != NULL); 162 EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id()); 163 } 164 165 TEST_F(InstantPageTest, DispatchRequestToDeleteMostVisitedItem) { 166 page.reset(new FakePage(&delegate, "", NULL, false)); 167 page->SetContents(web_contents()); 168 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 169 GURL item_url("www.foo.com"); 170 int page_id = web_contents()->GetController().GetActiveEntry()->GetPageID(); 171 EXPECT_CALL(delegate, DeleteMostVisitedItem(item_url)).Times(1); 172 EXPECT_TRUE(page->OnMessageReceived( 173 ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(rvh()->GetRoutingID(), 174 page_id, item_url))); 175 } 176 177 TEST_F(InstantPageTest, DispatchRequestToUndoMostVisitedDeletion) { 178 page.reset(new FakePage(&delegate, "", NULL, false)); 179 page->SetContents(web_contents()); 180 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 181 GURL item_url("www.foo.com"); 182 int page_id = web_contents()->GetController().GetActiveEntry()->GetPageID(); 183 EXPECT_CALL(delegate, UndoMostVisitedDeletion(item_url)).Times(1); 184 EXPECT_TRUE(page->OnMessageReceived( 185 ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(rvh()->GetRoutingID(), 186 page_id, item_url))); 187 } 188 189 TEST_F(InstantPageTest, DispatchRequestToUndoAllMostVisitedDeletions) { 190 page.reset(new FakePage(&delegate, "", NULL, false)); 191 page->SetContents(web_contents()); 192 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 193 int page_id = web_contents()->GetController().GetActiveEntry()->GetPageID(); 194 EXPECT_CALL(delegate, UndoAllMostVisitedDeletions()).Times(1); 195 EXPECT_TRUE(page->OnMessageReceived( 196 ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( 197 rvh()->GetRoutingID(), page_id))); 198 } 199 200 TEST_F(InstantPageTest, IgnoreMessageReceivedFromIncognitoPage) { 201 page.reset(new FakePage(&delegate, "", NULL, true)); 202 page->SetContents(web_contents()); 203 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 204 GURL item_url("www.foo.com"); 205 int page_id = web_contents()->GetController().GetActiveEntry()->GetPageID(); 206 207 EXPECT_CALL(delegate, DeleteMostVisitedItem(item_url)).Times(0); 208 EXPECT_FALSE(page->OnMessageReceived( 209 ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(rvh()->GetRoutingID(), 210 page_id, 211 item_url))); 212 213 EXPECT_CALL(delegate, UndoMostVisitedDeletion(item_url)).Times(0); 214 EXPECT_FALSE(page->OnMessageReceived( 215 ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(rvh()->GetRoutingID(), 216 page_id, 217 item_url))); 218 219 EXPECT_CALL(delegate, UndoAllMostVisitedDeletions()).Times(0); 220 EXPECT_FALSE(page->OnMessageReceived( 221 ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( 222 rvh()->GetRoutingID(), page_id))); 223 } 224 225 TEST_F(InstantPageTest, IgnoreMessageIfThePageIsNotActive) { 226 page.reset(new FakePage(&delegate, "", NULL, false)); 227 page->SetContents(web_contents()); 228 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 229 GURL item_url("www.foo.com"); 230 int inactive_page_id = 1999; 231 232 EXPECT_CALL(delegate, DeleteMostVisitedItem(item_url)).Times(0); 233 EXPECT_TRUE(page->OnMessageReceived( 234 ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(rvh()->GetRoutingID(), 235 inactive_page_id, 236 item_url))); 237 238 EXPECT_CALL(delegate, UndoMostVisitedDeletion(item_url)).Times(0); 239 EXPECT_TRUE(page->OnMessageReceived( 240 ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(rvh()->GetRoutingID(), 241 inactive_page_id, 242 item_url))); 243 244 EXPECT_CALL(delegate, UndoAllMostVisitedDeletions()).Times(0); 245 EXPECT_TRUE(page->OnMessageReceived( 246 ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( 247 rvh()->GetRoutingID(), inactive_page_id))); 248 } 249 250 TEST_F(InstantPageTest, IgnoreMessageReceivedFromThePage) { 251 page.reset(new FakePage(&delegate, "", NULL, false)); 252 page->SetContents(web_contents()); 253 254 // Ignore the messages received from the page. 255 page->set_should_handle_messages(false); 256 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 257 GURL item_url("www.foo.com"); 258 int page_id = web_contents()->GetController().GetActiveEntry()->GetPageID(); 259 260 EXPECT_CALL(delegate, DeleteMostVisitedItem(item_url)).Times(0); 261 EXPECT_TRUE(page->OnMessageReceived( 262 ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(rvh()->GetRoutingID(), 263 page_id, item_url))); 264 265 EXPECT_CALL(delegate, UndoMostVisitedDeletion(item_url)).Times(0); 266 EXPECT_TRUE(page->OnMessageReceived( 267 ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(rvh()->GetRoutingID(), 268 page_id, item_url))); 269 270 EXPECT_CALL(delegate, UndoAllMostVisitedDeletions()).Times(0); 271 EXPECT_TRUE(page->OnMessageReceived( 272 ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( 273 rvh()->GetRoutingID(), page_id))); 274 } 275 276 TEST_F(InstantPageTest, PageURLDoesntBelongToInstantRenderer) { 277 page.reset(new FakePage(&delegate, "", NULL, false)); 278 EXPECT_FALSE(page->supports_instant()); 279 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 280 page->SetContents(web_contents()); 281 282 // Navigate to a page URL that doesn't belong to Instant renderer. 283 // SearchTabHelper::DeterminerIfPageSupportsInstant() should return 284 // immediately without dispatching any message to the renderer. 285 NavigateAndCommit(GURL("http://www.example.com")); 286 EXPECT_FALSE(page->IsLocal()); 287 process()->sink().ClearMessages(); 288 EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), false)) 289 .Times(1); 290 291 SearchTabHelper::FromWebContents(web_contents())-> 292 DetermineIfPageSupportsInstant(); 293 const IPC::Message* message = process()->sink().GetFirstMessageMatching( 294 ChromeViewMsg_DetermineIfPageSupportsInstant::ID); 295 ASSERT_TRUE(message == NULL); 296 EXPECT_FALSE(page->supports_instant()); 297 } 298 299 // Test to verify that ChromeViewMsg_DetermineIfPageSupportsInstant message 300 // reply handler updates the instant support state in InstantPage. 301 TEST_F(InstantPageTest, PageSupportsInstant) { 302 page.reset(new FakePage(&delegate, "", NULL, false)); 303 EXPECT_FALSE(page->supports_instant()); 304 page->SetContents(web_contents()); 305 NavigateAndCommit(GURL("chrome-search://foo/bar")); 306 process()->sink().ClearMessages(); 307 SearchTabHelper::FromWebContents(web_contents())-> 308 DetermineIfPageSupportsInstant(); 309 const IPC::Message* message = process()->sink().GetFirstMessageMatching( 310 ChromeViewMsg_DetermineIfPageSupportsInstant::ID); 311 ASSERT_TRUE(message != NULL); 312 EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id()); 313 314 EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true)) 315 .Times(1); 316 317 // Assume the page supports instant. Invoke the message reply handler to make 318 // sure the InstantPage is notified about the instant support state. 319 const content::NavigationEntry* entry = 320 web_contents()->GetController().GetActiveEntry(); 321 EXPECT_TRUE(entry); 322 SearchTabHelper::FromWebContents(web_contents())-> 323 OnInstantSupportDetermined(entry->GetPageID(), true); 324 EXPECT_TRUE(page->supports_instant()); 325 } 326 327 TEST_F(InstantPageTest, AppropriateMessagesSentToIncognitoPages) { 328 page.reset(new FakePage(&delegate, "", NULL, true)); 329 page->SetContents(web_contents()); 330 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 331 process()->sink().ClearMessages(); 332 333 // Incognito pages should get these messages. 334 page->sender()->Submit(string16()); 335 EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID)); 336 page->sender()->SetOmniboxBounds(gfx::Rect()); 337 EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxMarginChange::ID)); 338 page->sender()->ToggleVoiceSearch(); 339 EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxToggleVoiceSearch::ID)); 340 341 // Incognito pages should not get any others. 342 page->sender()->SetFontInformation(string16(), 0); 343 EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxFontInformation::ID)); 344 345 page->sender()->SendThemeBackgroundInfo(ThemeBackgroundInfo()); 346 EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxThemeChanged::ID)); 347 348 page->sender()->FocusChanged( 349 OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT); 350 EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxFocusChanged::ID)); 351 352 page->sender()->SetInputInProgress(false); 353 EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxSetInputInProgress::ID)); 354 355 page->sender()->SendMostVisitedItems(std::vector<InstantMostVisitedItem>()); 356 EXPECT_FALSE(MessageWasSent( 357 ChromeViewMsg_SearchBoxMostVisitedItemsChanged::ID)); 358 } 359