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
23class Profile;
24
25namespace {
26
27class 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_METHOD5(NavigateToURL,
41               void(const content::WebContents* contents,
42                    const GURL& url,
43                    content::PageTransition transition,
44                    WindowOpenDisposition disposition,
45                    bool is_search_type));
46  MOCK_METHOD1(InstantPageLoadFailed, void(content::WebContents* contents));
47};
48
49}  // namespace
50
51class InstantPageTest : public ChromeRenderViewHostTestHarness {
52 public:
53  virtual void SetUp() OVERRIDE;
54
55  bool MessageWasSent(uint32 id) {
56    return process()->sink().GetFirstMessageMatching(id) != NULL;
57  }
58
59  scoped_ptr<InstantPage> page;
60  FakePageDelegate delegate;
61};
62
63void InstantPageTest::SetUp() {
64  ChromeRenderViewHostTestHarness::SetUp();
65  SearchTabHelper::CreateForWebContents(web_contents());
66}
67
68TEST_F(InstantPageTest, IsLocal) {
69  page.reset(new InstantPage(&delegate, "", NULL, false));
70  EXPECT_FALSE(page->supports_instant());
71  EXPECT_FALSE(page->IsLocal());
72  page->SetContents(web_contents());
73  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
74  EXPECT_TRUE(page->IsLocal());
75  NavigateAndCommit(GURL("http://example.com"));
76  EXPECT_FALSE(page->IsLocal());
77}
78
79TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_Local) {
80  page.reset(new InstantPage(&delegate, "", NULL, false));
81  EXPECT_FALSE(page->supports_instant());
82  page->SetContents(web_contents());
83  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
84  EXPECT_TRUE(page->IsLocal());
85  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true))
86      .Times(1);
87  SearchTabHelper::FromWebContents(web_contents())->
88      DetermineIfPageSupportsInstant();
89  EXPECT_TRUE(page->supports_instant());
90}
91
92TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_NonLocal) {
93  page.reset(new InstantPage(&delegate, "", NULL, false));
94  EXPECT_FALSE(page->supports_instant());
95  page->SetContents(web_contents());
96  NavigateAndCommit(GURL("chrome-search://foo/bar"));
97  EXPECT_FALSE(page->IsLocal());
98  process()->sink().ClearMessages();
99  SearchTabHelper::FromWebContents(web_contents())->
100      DetermineIfPageSupportsInstant();
101  const IPC::Message* message = process()->sink().GetFirstMessageMatching(
102      ChromeViewMsg_DetermineIfPageSupportsInstant::ID);
103  ASSERT_TRUE(message != NULL);
104  EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id());
105}
106
107TEST_F(InstantPageTest, PageURLDoesntBelongToInstantRenderer) {
108  page.reset(new InstantPage(&delegate, "", NULL, false));
109  EXPECT_FALSE(page->supports_instant());
110  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
111  page->SetContents(web_contents());
112
113  // Navigate to a page URL that doesn't belong to Instant renderer.
114  // SearchTabHelper::DeterminerIfPageSupportsInstant() should return
115  // immediately without dispatching any message to the renderer.
116  NavigateAndCommit(GURL("http://www.example.com"));
117  EXPECT_FALSE(page->IsLocal());
118  process()->sink().ClearMessages();
119  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), false))
120      .Times(1);
121
122  SearchTabHelper::FromWebContents(web_contents())->
123      DetermineIfPageSupportsInstant();
124  const IPC::Message* message = process()->sink().GetFirstMessageMatching(
125      ChromeViewMsg_DetermineIfPageSupportsInstant::ID);
126  ASSERT_TRUE(message == NULL);
127  EXPECT_FALSE(page->supports_instant());
128}
129
130// Test to verify that ChromeViewMsg_DetermineIfPageSupportsInstant message
131// reply handler updates the instant support state in InstantPage.
132TEST_F(InstantPageTest, PageSupportsInstant) {
133  page.reset(new InstantPage(&delegate, "", NULL, false));
134  EXPECT_FALSE(page->supports_instant());
135  page->SetContents(web_contents());
136  NavigateAndCommit(GURL("chrome-search://foo/bar"));
137  process()->sink().ClearMessages();
138  SearchTabHelper::FromWebContents(web_contents())->
139      DetermineIfPageSupportsInstant();
140  const IPC::Message* message = process()->sink().GetFirstMessageMatching(
141      ChromeViewMsg_DetermineIfPageSupportsInstant::ID);
142  ASSERT_TRUE(message != NULL);
143  EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id());
144
145  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true))
146      .Times(1);
147
148  // Assume the page supports instant. Invoke the message reply handler to make
149  // sure the InstantPage is notified about the instant support state.
150  const content::NavigationEntry* entry =
151      web_contents()->GetController().GetLastCommittedEntry();
152  EXPECT_TRUE(entry);
153  SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true);
154  EXPECT_TRUE(page->supports_instant());
155}
156
157TEST_F(InstantPageTest, AppropriateMessagesSentToIncognitoPages) {
158  page.reset(new InstantPage(&delegate, "", NULL, true));
159  page->SetContents(web_contents());
160  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
161  process()->sink().ClearMessages();
162
163  // Incognito pages should get these messages.
164  page->sender()->SetOmniboxBounds(gfx::Rect());
165  EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxMarginChange::ID));
166
167  // Incognito pages should not get any others.
168  page->sender()->FocusChanged(
169      OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT);
170  EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxFocusChanged::ID));
171
172  page->sender()->SetInputInProgress(false);
173  EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxSetInputInProgress::ID));
174}
175