Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / search / instant_controller.cc
1 // Copyright 2012 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_controller.h"
6
7 #include "base/prefs/pref_service.h"
8 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/content_settings/content_settings_provider.h"
11 #include "chrome/browser/content_settings/host_content_settings_map.h"
12 #include "chrome/browser/platform_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/search/instant_service.h"
15 #include "chrome/browser/search/instant_service_factory.h"
16 #include "chrome/browser/search/search.h"
17 #include "chrome/browser/search_engines/search_terms_data.h"
18 #include "chrome/browser/search_engines/template_url_service.h"
19 #include "chrome/browser/search_engines/template_url_service_factory.h"
20 #include "chrome/browser/ui/browser_instant_controller.h"
21 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
22 #include "chrome/browser/ui/search/instant_tab.h"
23 #include "chrome/browser/ui/search/search_tab_helper.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/content_settings_types.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/common/search_urls.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/sessions/serialized_navigation_entry.h"
30 #include "content/public/browser/navigation_entry.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_widget_host_view.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_view.h"
36 #include "net/base/escape.h"
37 #include "net/base/network_change_notifier.h"
38 #include "url/gurl.h"
39
40 #if defined(TOOLKIT_VIEWS)
41 #include "ui/views/widget/widget.h"
42 #endif
43
44 namespace {
45
46 bool IsContentsFrom(const InstantPage* page,
47                     const content::WebContents* contents) {
48   return page && (page->contents() == contents);
49 }
50
51 // Adds a transient NavigationEntry to the supplied |contents|'s
52 // NavigationController if the page's URL has not already been updated with the
53 // supplied |search_terms|. Sets the |search_terms| on the transient entry for
54 // search terms extraction to work correctly.
55 void EnsureSearchTermsAreSet(content::WebContents* contents,
56                              const base::string16& search_terms) {
57   content::NavigationController* controller = &contents->GetController();
58
59   // If search terms are already correct or there is already a transient entry
60   // (there shouldn't be), bail out early.
61   if (chrome::GetSearchTerms(contents) == search_terms ||
62       controller->GetTransientEntry())
63     return;
64
65   const content::NavigationEntry* entry = controller->GetLastCommittedEntry();
66   content::NavigationEntry* transient = controller->CreateNavigationEntry(
67       entry->GetURL(),
68       entry->GetReferrer(),
69       entry->GetTransitionType(),
70       false,
71       std::string(),
72       contents->GetBrowserContext());
73   transient->SetExtraData(sessions::kSearchTermsKey, search_terms);
74   controller->SetTransientEntry(transient);
75
76   SearchTabHelper::FromWebContents(contents)->NavigationEntryUpdated();
77 }
78
79 }  // namespace
80
81 InstantController::InstantController(BrowserInstantController* browser)
82     : browser_(browser),
83       omnibox_focus_state_(OMNIBOX_FOCUS_NONE),
84       omnibox_focus_change_reason_(OMNIBOX_FOCUS_CHANGE_EXPLICIT) {
85 }
86
87 InstantController::~InstantController() {
88 }
89
90 void InstantController::SetSuggestionToPrefetch(
91     const InstantSuggestion& suggestion) {
92   if (instant_tab_ &&
93       SearchTabHelper::FromWebContents(instant_tab_->contents())->
94           IsSearchResultsPage()) {
95     if (chrome::ShouldPrefetchSearchResultsOnSRP() ||
96         chrome::ShouldPrefetchSearchResults()) {
97       SearchTabHelper::FromWebContents(instant_tab_->contents())->
98           SetSuggestionToPrefetch(suggestion);
99     }
100   } else {
101     if (chrome::ShouldPrefetchSearchResults()) {
102       InstantSearchPrerenderer* prerenderer =
103           InstantSearchPrerenderer::GetForProfile(profile());
104       if (prerenderer)
105         prerenderer->Prerender(suggestion);
106     }
107   }
108 }
109
110 bool InstantController::SubmitQuery(const base::string16& search_terms) {
111   if (instant_tab_ && instant_tab_->supports_instant() &&
112       search_mode_.is_origin_search()) {
113     // Use |instant_tab_| to run the query if we're already on a search results
114     // page. (NOTE: in particular, we do not send the query to NTPs.)
115     SearchTabHelper::FromWebContents(instant_tab_->contents())->Submit(
116         search_terms);
117     instant_tab_->contents()->GetView()->Focus();
118     EnsureSearchTermsAreSet(instant_tab_->contents(), search_terms);
119     return true;
120   }
121   return false;
122 }
123
124 void InstantController::OmniboxFocusChanged(
125     OmniboxFocusState state,
126     OmniboxFocusChangeReason reason,
127     gfx::NativeView view_gaining_focus) {
128   LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
129       "OmniboxFocusChanged: %d to %d for reason %d", omnibox_focus_state_,
130       state, reason));
131
132   omnibox_focus_state_ = state;
133   if (!instant_tab_)
134     return;
135
136   content::NotificationService::current()->Notify(
137       chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
138       content::Source<InstantController>(this),
139       content::NotificationService::NoDetails());
140
141   instant_tab_->sender()->FocusChanged(omnibox_focus_state_, reason);
142   // Don't send oninputstart/oninputend updates in response to focus changes
143   // if there's a navigation in progress. This prevents Chrome from sending
144   // a spurious oninputend when the user accepts a match in the omnibox.
145   if (instant_tab_->contents()->GetController().GetPendingEntry() == NULL)
146     instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
147 }
148
149 void InstantController::SearchModeChanged(const SearchMode& old_mode,
150                                           const SearchMode& new_mode) {
151   LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
152       "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin,
153       old_mode.mode, new_mode.origin, new_mode.mode));
154
155   search_mode_ = new_mode;
156   ResetInstantTab();
157
158   if (instant_tab_ && old_mode.is_ntp() != new_mode.is_ntp())
159     instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
160 }
161
162 void InstantController::ActiveTabChanged() {
163   LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged");
164   ResetInstantTab();
165 }
166
167 void InstantController::TabDeactivated(content::WebContents* contents) {
168   // If user is deactivating an NTP tab, log the number of mouseovers for this
169   // NTP session.
170   if (chrome::IsInstantNTP(contents))
171     InstantTab::EmitNtpStatistics(contents);
172 }
173
174 void InstantController::LogDebugEvent(const std::string& info) const {
175   DVLOG(1) << info;
176
177   debug_events_.push_front(std::make_pair(
178       base::Time::Now().ToInternalValue(), info));
179   static const size_t kMaxDebugEventSize = 2000;
180   if (debug_events_.size() > kMaxDebugEventSize)
181     debug_events_.pop_back();
182 }
183
184 void InstantController::ClearDebugEvents() {
185   debug_events_.clear();
186 }
187
188 Profile* InstantController::profile() const {
189   return browser_->profile();
190 }
191
192 InstantTab* InstantController::instant_tab() const {
193   return instant_tab_.get();
194 }
195
196 void InstantController::InstantSupportChanged(
197     InstantSupportState instant_support) {
198   // Handle INSTANT_SUPPORT_YES here because InstantPage is not hooked up to the
199   // active tab. Search model changed listener in InstantPage will handle other
200   // cases.
201   if (instant_support != INSTANT_SUPPORT_YES)
202     return;
203
204   ResetInstantTab();
205 }
206
207 void InstantController::InstantSupportDetermined(
208     const content::WebContents* contents,
209     bool supports_instant) {
210   DCHECK(IsContentsFrom(instant_tab(), contents));
211
212   if (!supports_instant)
213     base::MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release());
214
215   content::NotificationService::current()->Notify(
216       chrome::NOTIFICATION_INSTANT_TAB_SUPPORT_DETERMINED,
217       content::Source<InstantController>(this),
218       content::NotificationService::NoDetails());
219 }
220
221 void InstantController::InstantPageAboutToNavigateMainFrame(
222     const content::WebContents* contents,
223     const GURL& url) {
224   DCHECK(IsContentsFrom(instant_tab(), contents));
225
226   // The Instant tab navigated.  Send it the data it needs to display
227   // properly.
228   UpdateInfoForInstantTab();
229 }
230
231 void InstantController::ResetInstantTab() {
232   if (!search_mode_.is_origin_default()) {
233     content::WebContents* active_tab = browser_->GetActiveWebContents();
234     if (!instant_tab_ || active_tab != instant_tab_->contents()) {
235       instant_tab_.reset(new InstantTab(this, browser_->profile()));
236       instant_tab_->Init(active_tab);
237       UpdateInfoForInstantTab();
238     }
239   } else {
240     instant_tab_.reset();
241   }
242 }
243
244 void InstantController::UpdateInfoForInstantTab() {
245   if (instant_tab_) {
246     // Update theme details.
247     InstantService* instant_service = GetInstantService();
248     if (instant_service) {
249       instant_service->UpdateThemeInfo();
250       instant_service->UpdateMostVisitedItemsInfo();
251     }
252
253     instant_tab_->sender()->FocusChanged(omnibox_focus_state_,
254                                          omnibox_focus_change_reason_);
255     instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
256   }
257 }
258
259 bool InstantController::IsInputInProgress() const {
260   return !search_mode_.is_ntp() &&
261       omnibox_focus_state_ == OMNIBOX_FOCUS_VISIBLE;
262 }
263
264 InstantService* InstantController::GetInstantService() const {
265   return InstantServiceFactory::GetForProfile(profile());
266 }