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/search_tab_helper.h"
6
7#include <set>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/metrics/histogram.h"
11#include "base/strings/string16.h"
12#include "base/strings/string_util.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/search/instant_service.h"
16#include "chrome/browser/search/instant_service_factory.h"
17#include "chrome/browser/search/search.h"
18#include "chrome/browser/signin/signin_manager_factory.h"
19#include "chrome/browser/sync/profile_sync_service.h"
20#include "chrome/browser/sync/profile_sync_service_factory.h"
21#include "chrome/browser/ui/app_list/app_list_util.h"
22#include "chrome/browser/ui/browser_navigator.h"
23#include "chrome/browser/ui/browser_window.h"
24#include "chrome/browser/ui/location_bar/location_bar.h"
25#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
26#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
27#include "chrome/browser/ui/omnibox/omnibox_view.h"
28#include "chrome/browser/ui/search/instant_search_prerenderer.h"
29#include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
30#include "chrome/browser/ui/search/search_tab_helper_delegate.h"
31#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
32#include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
33#include "chrome/common/url_constants.h"
34#include "chrome/grit/generated_resources.h"
35#include "components/google/core/browser/google_util.h"
36#include "components/search/search.h"
37#include "components/signin/core/browser/signin_manager.h"
38#include "content/public/browser/navigation_controller.h"
39#include "content/public/browser/navigation_details.h"
40#include "content/public/browser/navigation_entry.h"
41#include "content/public/browser/navigation_type.h"
42#include "content/public/browser/notification_service.h"
43#include "content/public/browser/notification_source.h"
44#include "content/public/browser/render_frame_host.h"
45#include "content/public/browser/render_process_host.h"
46#include "content/public/browser/user_metrics.h"
47#include "content/public/browser/web_contents.h"
48#include "content/public/common/referrer.h"
49#include "net/base/net_errors.h"
50#include "ui/base/l10n/l10n_util.h"
51#include "ui/base/page_transition_types.h"
52#include "url/gurl.h"
53
54DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);
55
56namespace {
57
58// For reporting Cacheable NTP navigations.
59enum CacheableNTPLoad {
60  CACHEABLE_NTP_LOAD_FAILED = 0,
61  CACHEABLE_NTP_LOAD_SUCCEEDED = 1,
62  CACHEABLE_NTP_LOAD_MAX = 2
63};
64
65void RecordCacheableNTPLoadHistogram(bool succeeded) {
66  UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
67                            succeeded ? CACHEABLE_NTP_LOAD_SUCCEEDED :
68                                CACHEABLE_NTP_LOAD_FAILED,
69                            CACHEABLE_NTP_LOAD_MAX);
70}
71
72bool IsCacheableNTP(const content::WebContents* contents) {
73  const content::NavigationEntry* entry =
74      contents->GetController().GetLastCommittedEntry();
75  return chrome::NavEntryIsInstantNTP(contents, entry) &&
76      entry->GetURL() != GURL(chrome::kChromeSearchLocalNtpUrl);
77}
78
79bool IsNTP(const content::WebContents* contents) {
80  // We can't use WebContents::GetURL() because that uses the active entry,
81  // whereas we want the visible entry.
82  const content::NavigationEntry* entry =
83      contents->GetController().GetVisibleEntry();
84  if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL))
85    return true;
86
87  return chrome::IsInstantNTP(contents);
88}
89
90bool IsSearchResults(const content::WebContents* contents) {
91  return !chrome::GetSearchTerms(contents).empty();
92}
93
94bool IsLocal(const content::WebContents* contents) {
95  if (!contents)
96    return false;
97  const content::NavigationEntry* entry =
98      contents->GetController().GetVisibleEntry();
99  return entry && entry->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl);
100}
101
102// Returns true if |contents| are rendered inside an Instant process.
103bool InInstantProcess(Profile* profile,
104                      const content::WebContents* contents) {
105  if (!profile || !contents)
106    return false;
107
108  InstantService* instant_service =
109      InstantServiceFactory::GetForProfile(profile);
110  return instant_service &&
111      instant_service->IsInstantProcess(
112          contents->GetRenderProcessHost()->GetID());
113}
114
115// Called when an NTP finishes loading. If the load start time was noted,
116// calculates and logs the total load time.
117void RecordNewTabLoadTime(content::WebContents* contents) {
118  CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
119  if (core_tab_helper->new_tab_start_time().is_null())
120    return;
121
122  base::TimeDelta duration =
123      base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
124  if (IsCacheableNTP(contents)) {
125    if (google_util::IsGoogleDomainUrl(
126        contents->GetController().GetLastCommittedEntry()->GetURL(),
127        google_util::ALLOW_SUBDOMAIN,
128        google_util::DISALLOW_NON_STANDARD_PORTS)) {
129      UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Google", duration);
130    } else {
131      UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Other", duration);
132    }
133  } else {
134    UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Local", duration);
135  }
136  core_tab_helper->set_new_tab_start_time(base::TimeTicks());
137}
138
139// Returns true if the user is signed in and full history sync is enabled,
140// and false otherwise.
141bool IsHistorySyncEnabled(Profile* profile) {
142  ProfileSyncService* sync =
143      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
144  return sync &&
145      sync->sync_initialized() &&
146      sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES);
147}
148
149bool OmniboxHasFocus(OmniboxView* omnibox) {
150  return omnibox && omnibox->model()->has_focus();
151}
152
153}  // namespace
154
155SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
156    : WebContentsObserver(web_contents),
157      is_search_enabled_(chrome::IsInstantExtendedAPIEnabled()),
158      web_contents_(web_contents),
159      ipc_router_(web_contents, this,
160                  make_scoped_ptr(new SearchIPCRouterPolicyImpl(web_contents))
161                      .PassAs<SearchIPCRouter::Policy>()),
162      instant_service_(NULL),
163      delegate_(NULL),
164      omnibox_has_focus_fn_(&OmniboxHasFocus) {
165  if (!is_search_enabled_)
166    return;
167
168  instant_service_ =
169      InstantServiceFactory::GetForProfile(
170          Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
171  if (instant_service_)
172    instant_service_->AddObserver(this);
173}
174
175SearchTabHelper::~SearchTabHelper() {
176  if (instant_service_)
177    instant_service_->RemoveObserver(this);
178}
179
180void SearchTabHelper::InitForPreloadedNTP() {
181  UpdateMode(true, true);
182}
183
184void SearchTabHelper::OmniboxInputStateChanged() {
185  if (!is_search_enabled_)
186    return;
187
188  UpdateMode(false, false);
189}
190
191void SearchTabHelper::OmniboxFocusChanged(OmniboxFocusState state,
192                                          OmniboxFocusChangeReason reason) {
193  content::NotificationService::current()->Notify(
194      chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
195      content::Source<SearchTabHelper>(this),
196      content::NotificationService::NoDetails());
197
198  ipc_router_.OmniboxFocusChanged(state, reason);
199
200  // Don't send oninputstart/oninputend updates in response to focus changes
201  // if there's a navigation in progress. This prevents Chrome from sending
202  // a spurious oninputend when the user accepts a match in the omnibox.
203  if (web_contents_->GetController().GetPendingEntry() == NULL) {
204    ipc_router_.SetInputInProgress(IsInputInProgress());
205
206    InstantSearchPrerenderer* prerenderer =
207        InstantSearchPrerenderer::GetForProfile(profile());
208    if (!prerenderer || !chrome::ShouldPrerenderInstantUrlOnOmniboxFocus())
209      return;
210
211    if (state == OMNIBOX_FOCUS_NONE) {
212      prerenderer->Cancel();
213      return;
214    }
215
216    if (!IsSearchResultsPage()) {
217      prerenderer->Init(
218          web_contents_->GetController().GetSessionStorageNamespaceMap(),
219          web_contents_->GetContainerBounds().size());
220    }
221  }
222}
223
224void SearchTabHelper::NavigationEntryUpdated() {
225  if (!is_search_enabled_)
226    return;
227
228  UpdateMode(false, false);
229}
230
231void SearchTabHelper::InstantSupportChanged(bool instant_support) {
232  if (!is_search_enabled_)
233    return;
234
235  InstantSupportState new_state = instant_support ? INSTANT_SUPPORT_YES :
236      INSTANT_SUPPORT_NO;
237
238  model_.SetInstantSupportState(new_state);
239
240  content::NavigationEntry* entry =
241      web_contents_->GetController().GetLastCommittedEntry();
242  if (entry) {
243    chrome::SetInstantSupportStateInNavigationEntry(new_state, entry);
244    if (delegate_ && !instant_support)
245      delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
246  }
247}
248
249bool SearchTabHelper::SupportsInstant() const {
250  return model_.instant_support() == INSTANT_SUPPORT_YES;
251}
252
253void SearchTabHelper::SetSuggestionToPrefetch(
254    const InstantSuggestion& suggestion) {
255  ipc_router_.SetSuggestionToPrefetch(suggestion);
256}
257
258void SearchTabHelper::Submit(const base::string16& text) {
259  ipc_router_.Submit(text);
260}
261
262void SearchTabHelper::OnTabActivated() {
263  ipc_router_.OnTabActivated();
264
265  OmniboxView* omnibox_view = GetOmniboxView();
266  if (chrome::ShouldPrerenderInstantUrlOnOmniboxFocus() &&
267      omnibox_has_focus_fn_(omnibox_view)) {
268    InstantSearchPrerenderer* prerenderer =
269        InstantSearchPrerenderer::GetForProfile(profile());
270    if (prerenderer && !IsSearchResultsPage()) {
271      prerenderer->Init(
272          web_contents_->GetController().GetSessionStorageNamespaceMap(),
273          web_contents_->GetContainerBounds().size());
274    }
275  }
276}
277
278void SearchTabHelper::OnTabDeactivated() {
279  ipc_router_.OnTabDeactivated();
280}
281
282void SearchTabHelper::ToggleVoiceSearch() {
283  ipc_router_.ToggleVoiceSearch();
284}
285
286bool SearchTabHelper::IsSearchResultsPage() {
287  return model_.mode().is_origin_search();
288}
289
290void SearchTabHelper::RenderViewCreated(
291    content::RenderViewHost* render_view_host) {
292  ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
293}
294
295void SearchTabHelper::DidStartNavigationToPendingEntry(
296    const GURL& url,
297    content::NavigationController::ReloadType /* reload_type */) {
298  if (chrome::IsNTPURL(url, profile())) {
299    // Set the title on any pending entry corresponding to the NTP. This
300    // prevents any flickering of the tab title.
301    content::NavigationEntry* entry =
302        web_contents_->GetController().GetPendingEntry();
303    if (entry)
304      entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
305  }
306}
307
308void SearchTabHelper::DidNavigateMainFrame(
309    const content::LoadCommittedDetails& details,
310    const content::FrameNavigateParams& params) {
311  if (IsCacheableNTP(web_contents_)) {
312    if (details.http_status_code == 204 || details.http_status_code >= 400) {
313      RedirectToLocalNTP();
314      RecordCacheableNTPLoadHistogram(false);
315      return;
316    }
317    RecordCacheableNTPLoadHistogram(true);
318  }
319
320  // Always set the title on the new tab page to be the one from our UI
321  // resources. Normally, we set the title when we begin a NTP load, but it can
322  // get reset in several places (like when you press Reload). This check
323  // ensures that the title is properly set to the string defined by the Chrome
324  // UI language (rather than the server language) in all cases.
325  //
326  // We only override the title when it's nonempty to allow the page to set the
327  // title if it really wants. An empty title means to use the default. There's
328  // also a race condition between this code and the page's SetTitle call which
329  // this rule avoids.
330  content::NavigationEntry* entry =
331      web_contents_->GetController().GetLastCommittedEntry();
332  if (entry && entry->GetTitle().empty() &&
333      (entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL) ||
334       chrome::NavEntryIsInstantNTP(web_contents_, entry))) {
335    entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
336  }
337}
338
339void SearchTabHelper::DidFailProvisionalLoad(
340    content::RenderFrameHost* render_frame_host,
341    const GURL& validated_url,
342    int error_code,
343    const base::string16& /* error_description */) {
344  // If error_code is ERR_ABORTED means that the user has canceled this
345  // navigation so it shouldn't be redirected.
346  if (!render_frame_host->GetParent() && error_code != net::ERR_ABORTED &&
347      validated_url != GURL(chrome::kChromeSearchLocalNtpUrl) &&
348      chrome::IsNTPURL(validated_url, profile())) {
349    RedirectToLocalNTP();
350    RecordCacheableNTPLoadHistogram(false);
351  }
352}
353
354void SearchTabHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
355                                    const GURL& /* validated_url */) {
356  if (!render_frame_host->GetParent()) {
357    if (chrome::IsInstantNTP(web_contents_))
358      RecordNewTabLoadTime(web_contents_);
359
360    DetermineIfPageSupportsInstant();
361  }
362}
363
364void SearchTabHelper::NavigationEntryCommitted(
365    const content::LoadCommittedDetails& load_details) {
366  if (!is_search_enabled_)
367    return;
368
369  if (!load_details.is_main_frame)
370    return;
371
372  if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
373                                               profile())) {
374    InstantService* instant_service =
375        InstantServiceFactory::GetForProfile(profile());
376    ipc_router_.SetOmniboxStartMargin(instant_service->omnibox_start_margin());
377    ipc_router_.SetDisplayInstantResults();
378  }
379
380  UpdateMode(true, false);
381
382  content::NavigationEntry* entry =
383      web_contents_->GetController().GetVisibleEntry();
384  DCHECK(entry);
385
386  // Already determined the instant support state for this page, do not reset
387  // the instant support state.
388  //
389  // When we get a navigation entry committed event, there seem to be two ways
390  // to tell whether the navigation was "in-page". Ideally, when
391  // LoadCommittedDetails::is_in_page is true, we should have
392  // LoadCommittedDetails::type to be NAVIGATION_TYPE_IN_PAGE. Unfortunately,
393  // they are different in some cases. To workaround this bug, we are checking
394  // (is_in_page || type == NAVIGATION_TYPE_IN_PAGE). Please refer to
395  // crbug.com/251330 for more details.
396  if (load_details.is_in_page ||
397      load_details.type == content::NAVIGATION_TYPE_IN_PAGE) {
398    // When an "in-page" navigation happens, we will not receive a
399    // DidFinishLoad() event. Therefore, we will not determine the Instant
400    // support for the navigated page. So, copy over the Instant support from
401    // the previous entry. If the page does not support Instant, update the
402    // location bar from here to turn off search terms replacement.
403    chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
404                                                    entry);
405    if (delegate_ && model_.instant_support() == INSTANT_SUPPORT_NO)
406      delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
407    return;
408  }
409
410  model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
411  model_.SetVoiceSearchSupported(false);
412  chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
413                                                  entry);
414
415  if (InInstantProcess(profile(), web_contents_))
416    ipc_router_.OnNavigationEntryCommitted();
417}
418
419void SearchTabHelper::OnInstantSupportDetermined(bool supports_instant) {
420  InstantSupportChanged(supports_instant);
421}
422
423void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
424  model_.SetVoiceSearchSupported(supports_voice_search);
425}
426
427void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
428  ipc_router_.SendThemeBackgroundInfo(theme_info);
429}
430
431void SearchTabHelper::MostVisitedItemsChanged(
432    const std::vector<InstantMostVisitedItem>& items) {
433  ipc_router_.SendMostVisitedItems(items);
434}
435
436void SearchTabHelper::OmniboxStartMarginChanged(int omnibox_start_margin) {
437  ipc_router_.SetOmniboxStartMargin(omnibox_start_margin);
438}
439
440void SearchTabHelper::FocusOmnibox(OmniboxFocusState state) {
441// TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
442#if !defined(OS_ANDROID)
443  OmniboxView* omnibox = GetOmniboxView();
444  if (!omnibox)
445    return;
446
447  // Do not add a default case in the switch block for the following reasons:
448  // (1) Explicitly handle the new states. If new states are added in the
449  // OmniboxFocusState, the compiler will warn the developer to handle the new
450  // states.
451  // (2) An attacker may control the renderer and sends the browser process a
452  // malformed IPC. This function responds to the invalid |state| values by
453  // doing nothing instead of crashing the browser process (intentional no-op).
454  switch (state) {
455    case OMNIBOX_FOCUS_VISIBLE:
456      omnibox->SetFocus();
457      omnibox->model()->SetCaretVisibility(true);
458      break;
459    case OMNIBOX_FOCUS_INVISIBLE:
460      omnibox->SetFocus();
461      omnibox->model()->SetCaretVisibility(false);
462      // If the user clicked on the fakebox, any text already in the omnibox
463      // should get cleared when they start typing. Selecting all the existing
464      // text is a convenient way to accomplish this. It also gives a slight
465      // visual cue to users who really understand selection state about what
466      // will happen if they start typing.
467      omnibox->SelectAll(false);
468      omnibox->ShowImeIfNeeded();
469      break;
470    case OMNIBOX_FOCUS_NONE:
471      // Remove focus only if the popup is closed. This will prevent someone
472      // from changing the omnibox value and closing the popup without user
473      // interaction.
474      if (!omnibox->model()->popup_model()->IsOpen())
475        web_contents()->Focus();
476      break;
477  }
478#endif
479}
480
481void SearchTabHelper::NavigateToURL(const GURL& url,
482                                    WindowOpenDisposition disposition,
483                                    bool is_most_visited_item_url) {
484  if (is_most_visited_item_url) {
485    content::RecordAction(
486        base::UserMetricsAction("InstantExtended.MostVisitedClicked"));
487  }
488
489  if (delegate_)
490    delegate_->NavigateOnThumbnailClick(url, disposition, web_contents_);
491}
492
493void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
494  DCHECK(!url.is_empty());
495  if (instant_service_)
496    instant_service_->DeleteMostVisitedItem(url);
497}
498
499void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
500  DCHECK(!url.is_empty());
501  if (instant_service_)
502    instant_service_->UndoMostVisitedDeletion(url);
503}
504
505void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
506  if (instant_service_)
507    instant_service_->UndoAllMostVisitedDeletions();
508}
509
510void SearchTabHelper::OnLogEvent(NTPLoggingEventType event) {
511// TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
512#if !defined(OS_ANDROID)
513  NTPUserDataLogger::GetOrCreateFromWebContents(
514      web_contents())->LogEvent(event);
515#endif
516}
517
518void SearchTabHelper::OnLogMostVisitedImpression(
519    int position, const base::string16& provider) {
520// TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
521#if !defined(OS_ANDROID)
522  NTPUserDataLogger::GetOrCreateFromWebContents(
523      web_contents())->LogMostVisitedImpression(position, provider);
524#endif
525}
526
527void SearchTabHelper::OnLogMostVisitedNavigation(
528    int position, const base::string16& provider) {
529// TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
530#if !defined(OS_ANDROID)
531  NTPUserDataLogger::GetOrCreateFromWebContents(
532      web_contents())->LogMostVisitedNavigation(position, provider);
533#endif
534}
535
536void SearchTabHelper::PasteIntoOmnibox(const base::string16& text) {
537// TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
538#if !defined(OS_ANDROID)
539  OmniboxView* omnibox = GetOmniboxView();
540  if (!omnibox)
541    return;
542  // The first case is for right click to paste, where the text is retrieved
543  // from the clipboard already sanitized. The second case is needed to handle
544  // drag-and-drop value and it has to be sanitazed before setting it into the
545  // omnibox.
546  base::string16 text_to_paste = text.empty() ? omnibox->GetClipboardText() :
547      omnibox->SanitizeTextForPaste(text);
548
549  if (text_to_paste.empty())
550    return;
551
552  if (!omnibox->model()->has_focus())
553    omnibox->SetFocus();
554
555  omnibox->OnBeforePossibleChange();
556  omnibox->model()->OnPaste();
557  omnibox->SetUserText(text_to_paste);
558  omnibox->OnAfterPossibleChange();
559#endif
560}
561
562void SearchTabHelper::OnChromeIdentityCheck(const base::string16& identity) {
563  SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile());
564  if (manager) {
565    const base::string16 username =
566        base::UTF8ToUTF16(manager->GetAuthenticatedUsername());
567    // The identity check only passes if the user is syncing their history.
568    // TODO(beaudoin): Change this function name and related APIs now that it's
569    // checking both the identity and the user's sync state.
570    bool matches = IsHistorySyncEnabled(profile()) && identity == username;
571    ipc_router_.SendChromeIdentityCheckResult(identity, matches);
572  }
573}
574
575void SearchTabHelper::UpdateMode(bool update_origin, bool is_preloaded_ntp) {
576  SearchMode::Type type = SearchMode::MODE_DEFAULT;
577  SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT;
578  if (IsNTP(web_contents_) || is_preloaded_ntp) {
579    type = SearchMode::MODE_NTP;
580    origin = SearchMode::ORIGIN_NTP;
581  } else if (IsSearchResults(web_contents_)) {
582    type = SearchMode::MODE_SEARCH_RESULTS;
583    origin = SearchMode::ORIGIN_SEARCH;
584  }
585  if (!update_origin)
586    origin = model_.mode().origin;
587
588  OmniboxView* omnibox = GetOmniboxView();
589  if (omnibox && omnibox->model()->user_input_in_progress())
590    type = SearchMode::MODE_SEARCH_SUGGESTIONS;
591
592  SearchMode old_mode(model_.mode());
593  model_.SetMode(SearchMode(type, origin));
594  if (old_mode.is_ntp() != model_.mode().is_ntp()) {
595    ipc_router_.SetInputInProgress(IsInputInProgress());
596  }
597}
598
599void SearchTabHelper::DetermineIfPageSupportsInstant() {
600  if (!InInstantProcess(profile(), web_contents_)) {
601    // The page is not in the Instant process. This page does not support
602    // instant. If we send an IPC message to a page that is not in the Instant
603    // process, it will never receive it and will never respond. Therefore,
604    // return immediately.
605    InstantSupportChanged(false);
606  } else if (IsLocal(web_contents_)) {
607    // Local pages always support Instant.
608    InstantSupportChanged(true);
609  } else {
610    ipc_router_.DetermineIfPageSupportsInstant();
611  }
612}
613
614Profile* SearchTabHelper::profile() const {
615  return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
616}
617
618void SearchTabHelper::RedirectToLocalNTP() {
619  // Extra parentheses to declare a variable.
620  content::NavigationController::LoadURLParams load_params(
621      (GURL(chrome::kChromeSearchLocalNtpUrl)));
622  load_params.referrer = content::Referrer();
623  load_params.transition_type = ui::PAGE_TRANSITION_SERVER_REDIRECT;
624  // Don't push a history entry.
625  load_params.should_replace_current_entry = true;
626  web_contents_->GetController().LoadURLWithParams(load_params);
627}
628
629bool SearchTabHelper::IsInputInProgress() const {
630  OmniboxView* omnibox = GetOmniboxView();
631  return !model_.mode().is_ntp() && omnibox &&
632      omnibox->model()->focus_state() == OMNIBOX_FOCUS_VISIBLE;
633}
634
635OmniboxView* SearchTabHelper::GetOmniboxView() const {
636  return delegate_ ? delegate_->GetOmniboxView() : NULL;
637}
638