Home | History | Annotate | Download | only in search
      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 
     54 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);
     55 
     56 namespace {
     57 
     58 // For reporting Cacheable NTP navigations.
     59 enum CacheableNTPLoad {
     60   CACHEABLE_NTP_LOAD_FAILED = 0,
     61   CACHEABLE_NTP_LOAD_SUCCEEDED = 1,
     62   CACHEABLE_NTP_LOAD_MAX = 2
     63 };
     64 
     65 void 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 
     72 bool 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 
     79 bool 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 
     90 bool IsSearchResults(const content::WebContents* contents) {
     91   return !chrome::GetSearchTerms(contents).empty();
     92 }
     93 
     94 bool 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.
    103 bool 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.
    117 void 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.
    141 bool 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 
    149 bool OmniboxHasFocus(OmniboxView* omnibox) {
    150   return omnibox && omnibox->model()->has_focus();
    151 }
    152 
    153 }  // namespace
    154 
    155 SearchTabHelper::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 
    175 SearchTabHelper::~SearchTabHelper() {
    176   if (instant_service_)
    177     instant_service_->RemoveObserver(this);
    178 }
    179 
    180 void SearchTabHelper::InitForPreloadedNTP() {
    181   UpdateMode(true, true);
    182 }
    183 
    184 void SearchTabHelper::OmniboxInputStateChanged() {
    185   if (!is_search_enabled_)
    186     return;
    187 
    188   UpdateMode(false, false);
    189 }
    190 
    191 void 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 
    224 void SearchTabHelper::NavigationEntryUpdated() {
    225   if (!is_search_enabled_)
    226     return;
    227 
    228   UpdateMode(false, false);
    229 }
    230 
    231 void 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 
    249 bool SearchTabHelper::SupportsInstant() const {
    250   return model_.instant_support() == INSTANT_SUPPORT_YES;
    251 }
    252 
    253 void SearchTabHelper::SetSuggestionToPrefetch(
    254     const InstantSuggestion& suggestion) {
    255   ipc_router_.SetSuggestionToPrefetch(suggestion);
    256 }
    257 
    258 void SearchTabHelper::Submit(const base::string16& text) {
    259   ipc_router_.Submit(text);
    260 }
    261 
    262 void 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 
    278 void SearchTabHelper::OnTabDeactivated() {
    279   ipc_router_.OnTabDeactivated();
    280 }
    281 
    282 void SearchTabHelper::ToggleVoiceSearch() {
    283   ipc_router_.ToggleVoiceSearch();
    284 }
    285 
    286 bool SearchTabHelper::IsSearchResultsPage() {
    287   return model_.mode().is_origin_search();
    288 }
    289 
    290 void SearchTabHelper::RenderViewCreated(
    291     content::RenderViewHost* render_view_host) {
    292   ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
    293 }
    294 
    295 void 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 
    308 void 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 
    339 void 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 
    354 void 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 
    364 void 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 
    419 void SearchTabHelper::OnInstantSupportDetermined(bool supports_instant) {
    420   InstantSupportChanged(supports_instant);
    421 }
    422 
    423 void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
    424   model_.SetVoiceSearchSupported(supports_voice_search);
    425 }
    426 
    427 void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
    428   ipc_router_.SendThemeBackgroundInfo(theme_info);
    429 }
    430 
    431 void SearchTabHelper::MostVisitedItemsChanged(
    432     const std::vector<InstantMostVisitedItem>& items) {
    433   ipc_router_.SendMostVisitedItems(items);
    434 }
    435 
    436 void SearchTabHelper::OmniboxStartMarginChanged(int omnibox_start_margin) {
    437   ipc_router_.SetOmniboxStartMargin(omnibox_start_margin);
    438 }
    439 
    440 void 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 
    481 void 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 
    493 void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
    494   DCHECK(!url.is_empty());
    495   if (instant_service_)
    496     instant_service_->DeleteMostVisitedItem(url);
    497 }
    498 
    499 void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
    500   DCHECK(!url.is_empty());
    501   if (instant_service_)
    502     instant_service_->UndoMostVisitedDeletion(url);
    503 }
    504 
    505 void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
    506   if (instant_service_)
    507     instant_service_->UndoAllMostVisitedDeletions();
    508 }
    509 
    510 void 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 
    518 void 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 
    527 void 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 
    536 void 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 
    562 void 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 
    575 void 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 
    599 void 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 
    614 Profile* SearchTabHelper::profile() const {
    615   return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
    616 }
    617 
    618 void 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 
    629 bool SearchTabHelper::IsInputInProgress() const {
    630   OmniboxView* omnibox = GetOmniboxView();
    631   return !model_.mode().is_ntp() && omnibox &&
    632       omnibox->model()->focus_state() == OMNIBOX_FOCUS_VISIBLE;
    633 }
    634 
    635 OmniboxView* SearchTabHelper::GetOmniboxView() const {
    636   return delegate_ ? delegate_->GetOmniboxView() : NULL;
    637 }
    638