root/chrome/browser/ui/search/search_tab_helper.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. RecordCacheableNTPLoadHistogram
  2. IsCacheableNTP
  3. IsNTP
  4. IsSearchResults
  5. IsLocal
  6. InInstantProcess
  7. UpdateLocationBar
  8. RecordNewTabLoadTime
  9. GetOmniboxView
  10. instant_service_
  11. InitForPreloadedNTP
  12. OmniboxInputStateChanged
  13. OmniboxFocusChanged
  14. NavigationEntryUpdated
  15. InstantSupportChanged
  16. SupportsInstant
  17. SetSuggestionToPrefetch
  18. Submit
  19. OnTabActivated
  20. OnTabDeactivated
  21. ToggleVoiceSearch
  22. IsSearchResultsPage
  23. RenderViewCreated
  24. DidStartNavigationToPendingEntry
  25. DidNavigateMainFrame
  26. DidFailProvisionalLoad
  27. DidFinishLoad
  28. NavigationEntryCommitted
  29. OnInstantSupportDetermined
  30. OnSetVoiceSearchSupport
  31. ThemeInfoChanged
  32. MostVisitedItemsChanged
  33. OmniboxStartMarginChanged
  34. MaybeRemoveMostVisitedItems
  35. FocusOmnibox
  36. NavigateToURL
  37. OnDeleteMostVisitedItem
  38. OnUndoMostVisitedDeletion
  39. OnUndoAllMostVisitedDeletions
  40. OnLogEvent
  41. OnLogMostVisitedImpression
  42. OnLogMostVisitedNavigation
  43. PasteIntoOmnibox
  44. OnChromeIdentityCheck
  45. UpdateMode
  46. DetermineIfPageSupportsInstant
  47. profile
  48. RedirectToLocalNTP
  49. IsInputInProgress

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/search/search_tab_helper.h"

#include <set>

#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/history/most_visited_tiles_experiment.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/app_list/app_list_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/omnibox/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
#include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
#include "chrome/common/url_constants.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_type.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/common/referrer.h"
#include "grit/generated_resources.h"
#include "net/base/net_errors.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"

DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);

namespace {

// For reporting Cacheable NTP navigations.
enum CacheableNTPLoad {
  CACHEABLE_NTP_LOAD_FAILED = 0,
  CACHEABLE_NTP_LOAD_SUCCEEDED = 1,
  CACHEABLE_NTP_LOAD_MAX = 2
};

void RecordCacheableNTPLoadHistogram(bool succeeded) {
  UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
                            succeeded ? CACHEABLE_NTP_LOAD_SUCCEEDED :
                                CACHEABLE_NTP_LOAD_FAILED,
                            CACHEABLE_NTP_LOAD_MAX);
}

bool IsCacheableNTP(const content::WebContents* contents) {
  const content::NavigationEntry* entry =
      contents->GetController().GetLastCommittedEntry();
  return chrome::NavEntryIsInstantNTP(contents, entry) &&
      entry->GetURL() != GURL(chrome::kChromeSearchLocalNtpUrl);
}

bool IsNTP(const content::WebContents* contents) {
  // We can't use WebContents::GetURL() because that uses the active entry,
  // whereas we want the visible entry.
  const content::NavigationEntry* entry =
      contents->GetController().GetVisibleEntry();
  if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL))
    return true;

  return chrome::IsInstantNTP(contents);
}

bool IsSearchResults(const content::WebContents* contents) {
  return !chrome::GetSearchTerms(contents).empty();
}

bool IsLocal(const content::WebContents* contents) {
  if (!contents)
    return false;
  const content::NavigationEntry* entry =
      contents->GetController().GetVisibleEntry();
  return entry && entry->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl);
}

// Returns true if |contents| are rendered inside an Instant process.
bool InInstantProcess(Profile* profile,
                      const content::WebContents* contents) {
  if (!profile || !contents)
    return false;

  InstantService* instant_service =
      InstantServiceFactory::GetForProfile(profile);
  return instant_service &&
      instant_service->IsInstantProcess(
          contents->GetRenderProcessHost()->GetID());
}

// Updates the location bar to reflect |contents| Instant support state.
void UpdateLocationBar(content::WebContents* contents) {
// iOS and Android don't use the Instant framework.
#if !defined(OS_IOS) && !defined(OS_ANDROID)
  if (!contents)
    return;

  Browser* browser = chrome::FindBrowserWithWebContents(contents);
  if (!browser)
    return;
  browser->OnWebContentsInstantSupportDisabled(contents);
#endif
}

// Called when an NTP finishes loading. If the load start time was noted,
// calculates and logs the total load time.
void RecordNewTabLoadTime(content::WebContents* contents) {
  CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
  if (core_tab_helper->new_tab_start_time().is_null())
    return;

  base::TimeDelta duration =
      base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
  UMA_HISTOGRAM_TIMES("Tab.NewTabOnload", duration);
  core_tab_helper->set_new_tab_start_time(base::TimeTicks());
}

// Returns the OmniboxView for |contents| or NULL if not available.
OmniboxView* GetOmniboxView(content::WebContents* contents) {
  // iOS and Android don't use the Instant framework.
#if defined(OS_IOS) || defined(OS_ANDROID)
  return NULL;
#else
  if (!contents)
    return NULL;
  Browser* browser = chrome::FindBrowserWithWebContents(contents);
  return browser ? browser->window()->GetLocationBar()->GetOmniboxView() : NULL;
#endif
}

}  // namespace

SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
    : WebContentsObserver(web_contents),
      is_search_enabled_(chrome::IsInstantExtendedAPIEnabled()),
      web_contents_(web_contents),
      ipc_router_(web_contents, this,
                  make_scoped_ptr(new SearchIPCRouterPolicyImpl(web_contents))
                      .PassAs<SearchIPCRouter::Policy>()),
      instant_service_(NULL) {
  if (!is_search_enabled_)
    return;

  instant_service_ =
      InstantServiceFactory::GetForProfile(
          Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
  if (instant_service_)
    instant_service_->AddObserver(this);
}

SearchTabHelper::~SearchTabHelper() {
  if (instant_service_)
    instant_service_->RemoveObserver(this);
}

void SearchTabHelper::InitForPreloadedNTP() {
  UpdateMode(true, true);
}

void SearchTabHelper::OmniboxInputStateChanged() {
  if (!is_search_enabled_)
    return;

  UpdateMode(false, false);
}

void SearchTabHelper::OmniboxFocusChanged(OmniboxFocusState state,
                                          OmniboxFocusChangeReason reason) {
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
      content::Source<SearchTabHelper>(this),
      content::NotificationService::NoDetails());

  ipc_router_.OmniboxFocusChanged(state, reason);

  // Don't send oninputstart/oninputend updates in response to focus changes
  // if there's a navigation in progress. This prevents Chrome from sending
  // a spurious oninputend when the user accepts a match in the omnibox.
  if (web_contents_->GetController().GetPendingEntry() == NULL) {
    ipc_router_.SetInputInProgress(IsInputInProgress());
  }
}

void SearchTabHelper::NavigationEntryUpdated() {
  if (!is_search_enabled_)
    return;

  UpdateMode(false, false);
}

void SearchTabHelper::InstantSupportChanged(bool instant_support) {
  if (!is_search_enabled_)
    return;

  InstantSupportState new_state = instant_support ? INSTANT_SUPPORT_YES :
      INSTANT_SUPPORT_NO;

  model_.SetInstantSupportState(new_state);

  content::NavigationEntry* entry =
      web_contents_->GetController().GetVisibleEntry();
  if (entry) {
    chrome::SetInstantSupportStateInNavigationEntry(new_state, entry);
    if (!instant_support)
      UpdateLocationBar(web_contents_);
  }
}

bool SearchTabHelper::SupportsInstant() const {
  return model_.instant_support() == INSTANT_SUPPORT_YES;
}

void SearchTabHelper::SetSuggestionToPrefetch(
    const InstantSuggestion& suggestion) {
  ipc_router_.SetSuggestionToPrefetch(suggestion);
}

void SearchTabHelper::Submit(const base::string16& text) {
  ipc_router_.Submit(text);
}

void SearchTabHelper::OnTabActivated() {
  ipc_router_.OnTabActivated();
}

void SearchTabHelper::OnTabDeactivated() {
  ipc_router_.OnTabDeactivated();
}

void SearchTabHelper::ToggleVoiceSearch() {
  ipc_router_.ToggleVoiceSearch();
}

bool SearchTabHelper::IsSearchResultsPage() {
  return model_.mode().is_origin_search();
}

void SearchTabHelper::RenderViewCreated(
    content::RenderViewHost* render_view_host) {
  ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
}

void SearchTabHelper::DidStartNavigationToPendingEntry(
    const GURL& url,
    content::NavigationController::ReloadType /* reload_type */) {
  if (chrome::IsNTPURL(url, profile())) {
    // Set the title on any pending entry corresponding to the NTP. This
    // prevents any flickering of the tab title.
    content::NavigationEntry* entry =
        web_contents_->GetController().GetPendingEntry();
    if (entry)
      entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
  }
}

void SearchTabHelper::DidNavigateMainFrame(
    const content::LoadCommittedDetails& details,
    const content::FrameNavigateParams& params) {
  if (IsCacheableNTP(web_contents_)) {
    if (details.http_status_code == 204 || details.http_status_code >= 400) {
      RedirectToLocalNTP();
      RecordCacheableNTPLoadHistogram(false);
      return;
    }
    RecordCacheableNTPLoadHistogram(true);
  }

  // Always set the title on the new tab page to be the one from our UI
  // resources. Normally, we set the title when we begin a NTP load, but it can
  // get reset in several places (like when you press Reload). This check
  // ensures that the title is properly set to the string defined by the Chrome
  // UI language (rather than the server language) in all cases.
  //
  // We only override the title when it's nonempty to allow the page to set the
  // title if it really wants. An empty title means to use the default. There's
  // also a race condition between this code and the page's SetTitle call which
  // this rule avoids.
  content::NavigationEntry* entry =
      web_contents_->GetController().GetLastCommittedEntry();
  if (entry && entry->GetTitle().empty() &&
      (entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL) ||
       chrome::NavEntryIsInstantNTP(web_contents_, entry))) {
    entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
  }
}

void SearchTabHelper::DidFailProvisionalLoad(
    int64 /* frame_id */,
    const base::string16& /* frame_unique_name */,
    bool is_main_frame,
    const GURL& validated_url,
    int error_code,
    const base::string16& /* error_description */,
    content::RenderViewHost* /* render_view_host */) {
  // If error_code is ERR_ABORTED means that the user has canceled this
  // navigation so it shouldn't be redirected.
  if (is_main_frame &&
      error_code != net::ERR_ABORTED &&
      validated_url != GURL(chrome::kChromeSearchLocalNtpUrl) &&
      chrome::IsNTPURL(validated_url, profile())) {
    RedirectToLocalNTP();
    RecordCacheableNTPLoadHistogram(false);
  }
}

void SearchTabHelper::DidFinishLoad(
    int64 /* frame_id */,
    const GURL&  /* validated_url */,
    bool is_main_frame,
    content::RenderViewHost* /* render_view_host */) {
  if (is_main_frame) {
    if (chrome::IsInstantNTP(web_contents_))
      RecordNewTabLoadTime(web_contents_);

    DetermineIfPageSupportsInstant();
  }
}

void SearchTabHelper::NavigationEntryCommitted(
    const content::LoadCommittedDetails& load_details) {
  if (!is_search_enabled_)
    return;

  if (!load_details.is_main_frame)
    return;

  if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
                                               profile())) {
    InstantService* instant_service =
        InstantServiceFactory::GetForProfile(profile());
    ipc_router_.SetOmniboxStartMargin(instant_service->omnibox_start_margin());
    ipc_router_.SetDisplayInstantResults();
  }

  UpdateMode(true, false);

  content::NavigationEntry* entry =
      web_contents_->GetController().GetVisibleEntry();
  DCHECK(entry);

  // Already determined the instant support state for this page, do not reset
  // the instant support state.
  //
  // When we get a navigation entry committed event, there seem to be two ways
  // to tell whether the navigation was "in-page". Ideally, when
  // LoadCommittedDetails::is_in_page is true, we should have
  // LoadCommittedDetails::type to be NAVIGATION_TYPE_IN_PAGE. Unfortunately,
  // they are different in some cases. To workaround this bug, we are checking
  // (is_in_page || type == NAVIGATION_TYPE_IN_PAGE). Please refer to
  // crbug.com/251330 for more details.
  if (load_details.is_in_page ||
      load_details.type == content::NAVIGATION_TYPE_IN_PAGE) {
    // When an "in-page" navigation happens, we will not receive a
    // DidFinishLoad() event. Therefore, we will not determine the Instant
    // support for the navigated page. So, copy over the Instant support from
    // the previous entry. If the page does not support Instant, update the
    // location bar from here to turn off search terms replacement.
    chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
                                                    entry);
    if (model_.instant_support() == INSTANT_SUPPORT_NO)
      UpdateLocationBar(web_contents_);
    return;
  }

  model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
  model_.SetVoiceSearchSupported(false);
  chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
                                                  entry);
}

void SearchTabHelper::OnInstantSupportDetermined(bool supports_instant) {
  InstantSupportChanged(supports_instant);
}

void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
  model_.SetVoiceSearchSupported(supports_voice_search);
}

void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
  ipc_router_.SendThemeBackgroundInfo(theme_info);
}

void SearchTabHelper::MostVisitedItemsChanged(
    const std::vector<InstantMostVisitedItem>& items) {
  std::vector<InstantMostVisitedItem> items_copy(items);
  MaybeRemoveMostVisitedItems(&items_copy);
  ipc_router_.SendMostVisitedItems(items_copy);
}

void SearchTabHelper::OmniboxStartMarginChanged(int omnibox_start_margin) {
  ipc_router_.SetOmniboxStartMargin(omnibox_start_margin);
}

void SearchTabHelper::MaybeRemoveMostVisitedItems(
    std::vector<InstantMostVisitedItem>* items) {
// The code below uses APIs not available on Android and the experiment should
// not run there.
#if !defined(OS_ANDROID)
  if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
    return;

  Profile* profile =
      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
  if (!profile)
    return;

  Browser* browser = chrome::FindBrowserWithProfile(profile,
                                                    chrome::GetActiveDesktop());
  if (!browser)
    return;

  TabStripModel* tab_strip_model = browser->tab_strip_model();
  history::TopSites* top_sites = profile->GetTopSites();
  if (!tab_strip_model || !top_sites) {
    NOTREACHED();
    return;
  }

  std::set<std::string> open_urls;
  chrome::GetOpenUrls(*tab_strip_model, *top_sites, &open_urls);
  history::MostVisitedTilesExperiment::RemoveItemsMatchingOpenTabs(
      open_urls, items);
#endif
}

void SearchTabHelper::FocusOmnibox(OmniboxFocusState state) {
  OmniboxView* omnibox = GetOmniboxView(web_contents());
  if (!omnibox)
    return;

  // Do not add a default case in the switch block for the following reasons:
  // (1) Explicitly handle the new states. If new states are added in the
  // OmniboxFocusState, the compiler will warn the developer to handle the new
  // states.
  // (2) An attacker may control the renderer and sends the browser process a
  // malformed IPC. This function responds to the invalid |state| values by
  // doing nothing instead of crashing the browser process (intentional no-op).
  switch (state) {
    case OMNIBOX_FOCUS_VISIBLE:
      omnibox->SetFocus();
      omnibox->model()->SetCaretVisibility(true);
      break;
    case OMNIBOX_FOCUS_INVISIBLE:
      omnibox->SetFocus();
      omnibox->model()->SetCaretVisibility(false);
      // If the user clicked on the fakebox, any text already in the omnibox
      // should get cleared when they start typing. Selecting all the existing
      // text is a convenient way to accomplish this. It also gives a slight
      // visual cue to users who really understand selection state about what
      // will happen if they start typing.
      omnibox->SelectAll(false);
      omnibox->ShowImeIfNeeded();
      break;
    case OMNIBOX_FOCUS_NONE:
      // Remove focus only if the popup is closed. This will prevent someone
      // from changing the omnibox value and closing the popup without user
      // interaction.
      if (!omnibox->model()->popup_model()->IsOpen())
        web_contents()->GetView()->Focus();
      break;
  }
}

void SearchTabHelper::NavigateToURL(const GURL& url,
                                    WindowOpenDisposition disposition,
                                    bool is_most_visited_item_url) {
// iOS and Android don't use the Instant framework.
#if !defined(OS_IOS) && !defined(OS_ANDROID)
  // TODO(kmadhusu): Remove chrome::FindBrowser...() function call from here.
  // Create a SearchTabHelperDelegate interface and have the Browser object
  // implement that interface to provide the necessary functionality.
  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
  Profile* profile =
      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
  if (!browser || !profile)
    return;

  if (is_most_visited_item_url) {
    content::RecordAction(
        base::UserMetricsAction("InstantExtended.MostVisitedClicked"));
  }

  chrome::NavigateParams params(browser, url,
                                content::PAGE_TRANSITION_AUTO_BOOKMARK);
  params.referrer = content::Referrer();
  params.source_contents = web_contents_;
  params.disposition = disposition;
  params.is_renderer_initiated = false;
  params.initiating_profile = profile;
  chrome::Navigate(&params);
#endif
}

void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
  DCHECK(!url.is_empty());
  if (instant_service_)
    instant_service_->DeleteMostVisitedItem(url);
}

void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
  DCHECK(!url.is_empty());
  if (instant_service_)
    instant_service_->UndoMostVisitedDeletion(url);
}

void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
  if (instant_service_)
    instant_service_->UndoAllMostVisitedDeletions();
}

void SearchTabHelper::OnLogEvent(NTPLoggingEventType event) {
  NTPUserDataLogger::GetOrCreateFromWebContents(
      web_contents())->LogEvent(event);
}

void SearchTabHelper::OnLogMostVisitedImpression(
    int position, const base::string16& provider) {
  NTPUserDataLogger::GetOrCreateFromWebContents(
      web_contents())->LogMostVisitedImpression(position, provider);
}

void SearchTabHelper::OnLogMostVisitedNavigation(
    int position, const base::string16& provider) {
  NTPUserDataLogger::GetOrCreateFromWebContents(
      web_contents())->LogMostVisitedNavigation(position, provider);
}

void SearchTabHelper::PasteIntoOmnibox(const base::string16& text) {
  OmniboxView* omnibox = GetOmniboxView(web_contents());
  if (!omnibox)
    return;

  // The first case is for right click to paste, where the text is retrieved
  // from the clipboard already sanitized. The second case is needed to handle
  // drag-and-drop value and it has to be sanitazed before setting it into the
  // omnibox.
  base::string16 text_to_paste = text.empty() ? omnibox->GetClipboardText() :
      omnibox->SanitizeTextForPaste(text);

  if (text_to_paste.empty())
    return;

  if (!omnibox->model()->has_focus())
    omnibox->SetFocus();

  omnibox->OnBeforePossibleChange();
  omnibox->model()->OnPaste();
  omnibox->SetUserText(text_to_paste);
  omnibox->OnAfterPossibleChange();
}

void SearchTabHelper::OnChromeIdentityCheck(const base::string16& identity) {
  SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile());
  if (manager) {
    const base::string16 username =
        base::UTF8ToUTF16(manager->GetAuthenticatedUsername());
    ipc_router_.SendChromeIdentityCheckResult(identity,
                                              identity == username);
  }
}

void SearchTabHelper::UpdateMode(bool update_origin, bool is_preloaded_ntp) {
  SearchMode::Type type = SearchMode::MODE_DEFAULT;
  SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT;
  if (IsNTP(web_contents_) || is_preloaded_ntp) {
    type = SearchMode::MODE_NTP;
    origin = SearchMode::ORIGIN_NTP;
  } else if (IsSearchResults(web_contents_)) {
    type = SearchMode::MODE_SEARCH_RESULTS;
    origin = SearchMode::ORIGIN_SEARCH;
  }
  if (!update_origin)
    origin = model_.mode().origin;

  OmniboxView* omnibox = GetOmniboxView(web_contents());
  if (omnibox && omnibox->model()->user_input_in_progress())
    type = SearchMode::MODE_SEARCH_SUGGESTIONS;

  SearchMode old_mode(model_.mode());
  model_.SetMode(SearchMode(type, origin));
  if (old_mode.is_ntp() != model_.mode().is_ntp()) {
    ipc_router_.SetInputInProgress(IsInputInProgress());
  }
}

void SearchTabHelper::DetermineIfPageSupportsInstant() {
  if (!InInstantProcess(profile(), web_contents_)) {
    // The page is not in the Instant process. This page does not support
    // instant. If we send an IPC message to a page that is not in the Instant
    // process, it will never receive it and will never respond. Therefore,
    // return immediately.
    InstantSupportChanged(false);
  } else if (IsLocal(web_contents_)) {
    // Local pages always support Instant.
    InstantSupportChanged(true);
  } else {
    ipc_router_.DetermineIfPageSupportsInstant();
  }
}

Profile* SearchTabHelper::profile() const {
  return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
}

void SearchTabHelper::RedirectToLocalNTP() {
  // Extra parentheses to declare a variable.
  content::NavigationController::LoadURLParams load_params(
      (GURL(chrome::kChromeSearchLocalNtpUrl)));
  load_params.referrer = content::Referrer();
  load_params.transition_type = content::PAGE_TRANSITION_SERVER_REDIRECT;
  // Don't push a history entry.
  load_params.should_replace_current_entry = true;
  web_contents_->GetController().LoadURLWithParams(load_params);
}

bool SearchTabHelper::IsInputInProgress() const {
  OmniboxView* omnibox = GetOmniboxView(web_contents());
  return !model_.mode().is_ntp() && omnibox &&
      omnibox->model()->focus_state() == OMNIBOX_FOCUS_VISIBLE;
}

/* [<][>][^][v][top][bottom][index][help] */