Home | History | Annotate | Download | only in ui
      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/browser_instant_controller.h"
      6 
      7 #include "base/bind.h"
      8 #include "chrome/browser/extensions/extension_service.h"
      9 #include "chrome/browser/extensions/extension_web_ui.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/browser/search/instant_service.h"
     12 #include "chrome/browser/search/instant_service_factory.h"
     13 #include "chrome/browser/search/search.h"
     14 #include "chrome/browser/ui/browser.h"
     15 #include "chrome/browser/ui/browser_window.h"
     16 #include "chrome/browser/ui/omnibox/location_bar.h"
     17 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
     18 #include "chrome/browser/ui/omnibox/omnibox_view.h"
     19 #include "chrome/browser/ui/search/instant_ntp.h"
     20 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
     21 #include "chrome/browser/ui/search/search_model.h"
     22 #include "chrome/browser/ui/search/search_tab_helper.h"
     23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     24 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
     25 #include "chrome/common/url_constants.h"
     26 #include "content/public/browser/render_process_host.h"
     27 #include "content/public/browser/user_metrics.h"
     28 #include "content/public/browser/web_contents.h"
     29 #include "content/public/browser/web_contents_view.h"
     30 
     31 using content::UserMetricsAction;
     32 
     33 namespace {
     34 
     35 InstantSearchPrerenderer* GetInstantSearchPrerenderer(Profile* profile) {
     36   DCHECK(profile);
     37   InstantService* instant_service =
     38       InstantServiceFactory::GetForProfile(profile);
     39   return instant_service ? instant_service->instant_search_prerenderer() : NULL;
     40 }
     41 
     42 }  // namespace
     43 
     44 ////////////////////////////////////////////////////////////////////////////////
     45 // BrowserInstantController, public:
     46 
     47 BrowserInstantController::BrowserInstantController(Browser* browser)
     48     : browser_(browser),
     49       instant_(this),
     50       instant_unload_handler_(browser) {
     51   browser_->search_model()->AddObserver(this);
     52 
     53   InstantService* instant_service =
     54       InstantServiceFactory::GetForProfile(profile());
     55   instant_service->OnBrowserInstantControllerCreated();
     56   instant_service->AddObserver(this);
     57 }
     58 
     59 BrowserInstantController::~BrowserInstantController() {
     60   browser_->search_model()->RemoveObserver(this);
     61 
     62   InstantService* instant_service =
     63       InstantServiceFactory::GetForProfile(profile());
     64   instant_service->RemoveObserver(this);
     65   instant_service->OnBrowserInstantControllerDestroyed();
     66 }
     67 
     68 bool BrowserInstantController::MaybeSwapInInstantNTPContents(
     69     const GURL& url,
     70     content::WebContents* source_contents,
     71     content::WebContents** target_contents) {
     72   if (url != GURL(chrome::kChromeUINewTabURL))
     73     return false;
     74 
     75   GURL extension_url(url);
     76   if (ExtensionWebUI::HandleChromeURLOverride(&extension_url, profile())) {
     77     // If there is an extension overriding the NTP do not use the Instant NTP.
     78     return false;
     79   }
     80 
     81   InstantService* instant_service =
     82       InstantServiceFactory::GetForProfile(profile());
     83   scoped_ptr<content::WebContents> instant_ntp =
     84       instant_service->ReleaseNTPContents();
     85   if (!instant_ntp)
     86     return false;
     87 
     88   *target_contents = instant_ntp.get();
     89   if (source_contents) {
     90     // If the Instant NTP hasn't yet committed an entry, we can't call
     91     // CopyStateFromAndPrune.  Instead, load the Local NTP URL directly in the
     92     // source contents.
     93     // TODO(sreeram): Always using the local URL is wrong in the case of the
     94     // first tab in a window where we might want to use the remote URL. Fix.
     95     if (!instant_ntp->GetController().CanPruneAllButLastCommitted()) {
     96       source_contents->GetController().LoadURL(chrome::GetLocalInstantURL(
     97           profile()), content::Referrer(), content::PAGE_TRANSITION_GENERATED,
     98           std::string());
     99       *target_contents = source_contents;
    100     } else {
    101       instant_ntp->GetController().CopyStateFromAndPrune(
    102           &source_contents->GetController(), false);
    103       ReplaceWebContentsAt(
    104           browser_->tab_strip_model()->GetIndexOfWebContents(source_contents),
    105           instant_ntp.Pass());
    106     }
    107   } else {
    108     // If the Instant NTP hasn't yet committed an entry, we can't call
    109     // PruneAllButLastCommitted.  In that case, there shouldn't be any entries
    110     // to prune anyway.
    111     if (instant_ntp->GetController().CanPruneAllButLastCommitted())
    112       instant_ntp->GetController().PruneAllButLastCommitted();
    113     else
    114       CHECK(!instant_ntp->GetController().GetLastCommittedEntry());
    115 
    116     // If |source_contents| is NULL, then the caller is responsible for
    117     // inserting instant_ntp into the tabstrip and will take ownership.
    118     ignore_result(instant_ntp.release());
    119   }
    120   return true;
    121 }
    122 
    123 bool BrowserInstantController::OpenInstant(WindowOpenDisposition disposition,
    124                                            const GURL& url) {
    125   // Unsupported dispositions.
    126   if (disposition == NEW_BACKGROUND_TAB || disposition == NEW_WINDOW ||
    127       disposition == NEW_FOREGROUND_TAB)
    128     return false;
    129 
    130   // The omnibox currently doesn't use other dispositions, so we don't attempt
    131   // to handle them. If you hit this DCHECK file a bug and I'll (sky) add
    132   // support for the new disposition.
    133   DCHECK(disposition == CURRENT_TAB) << disposition;
    134 
    135   // If we will not be replacing search terms from this URL, don't send to
    136   // InstantController.
    137   const base::string16& search_terms =
    138       chrome::GetSearchTermsFromURL(browser_->profile(), url);
    139   if (search_terms.empty())
    140     return false;
    141 
    142   InstantSearchPrerenderer* prerenderer =
    143       GetInstantSearchPrerenderer(profile());
    144   if (prerenderer &&
    145       prerenderer->CanCommitQuery(GetActiveWebContents(), search_terms)) {
    146     // Submit query to render the prefetched results. Browser will swap the
    147     // prerendered contents with the active tab contents.
    148     prerenderer->Commit(search_terms);
    149     return false;
    150   }
    151 
    152   return instant_.SubmitQuery(search_terms);
    153 }
    154 
    155 Profile* BrowserInstantController::profile() const {
    156   return browser_->profile();
    157 }
    158 
    159 void BrowserInstantController::ReplaceWebContentsAt(
    160     int index,
    161     scoped_ptr<content::WebContents> new_contents) {
    162   DCHECK_NE(TabStripModel::kNoTab, index);
    163   scoped_ptr<content::WebContents> old_contents(browser_->tab_strip_model()->
    164       ReplaceWebContentsAt(index, new_contents.release()));
    165   instant_unload_handler_.RunUnloadListenersOrDestroy(old_contents.Pass(),
    166                                                       index);
    167 }
    168 
    169 content::WebContents* BrowserInstantController::GetActiveWebContents() const {
    170   return browser_->tab_strip_model()->GetActiveWebContents();
    171 }
    172 
    173 void BrowserInstantController::ActiveTabChanged() {
    174   instant_.ActiveTabChanged();
    175 }
    176 
    177 void BrowserInstantController::TabDeactivated(content::WebContents* contents) {
    178   instant_.TabDeactivated(contents);
    179 
    180   InstantSearchPrerenderer* prerenderer =
    181       GetInstantSearchPrerenderer(profile());
    182   if (prerenderer)
    183     prerenderer->Cancel();
    184 }
    185 
    186 void BrowserInstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
    187   instant_.SetOmniboxBounds(bounds);
    188 }
    189 
    190 ////////////////////////////////////////////////////////////////////////////////
    191 // BrowserInstantController, SearchModelObserver implementation:
    192 
    193 void BrowserInstantController::ModelChanged(
    194     const SearchModel::State& old_state,
    195     const SearchModel::State& new_state) {
    196   if (old_state.mode != new_state.mode) {
    197     const SearchMode& new_mode = new_state.mode;
    198 
    199     // Record some actions corresponding to the mode change. Note that to get
    200     // the full story, it's necessary to look at other UMA actions as well,
    201     // such as tab switches.
    202     if (new_mode.is_search_results())
    203       content::RecordAction(UserMetricsAction("InstantExtended.ShowSRP"));
    204     else if (new_mode.is_ntp())
    205       content::RecordAction(UserMetricsAction("InstantExtended.ShowNTP"));
    206 
    207     instant_.SearchModeChanged(old_state.mode, new_mode);
    208   }
    209 
    210   if (old_state.instant_support != new_state.instant_support)
    211     instant_.InstantSupportChanged(new_state.instant_support);
    212 }
    213 
    214 ////////////////////////////////////////////////////////////////////////////////
    215 // BrowserInstantController, InstantServiceObserver implementation:
    216 
    217 void BrowserInstantController::DefaultSearchProviderChanged() {
    218   ReloadTabsInInstantProcess();
    219 }
    220 
    221 void BrowserInstantController::GoogleURLUpdated() {
    222   ReloadTabsInInstantProcess();
    223 }
    224 
    225 void BrowserInstantController::ReloadTabsInInstantProcess() {
    226   InstantService* instant_service =
    227       InstantServiceFactory::GetForProfile(profile());
    228   if (!instant_service)
    229     return;
    230 
    231   TabStripModel* tab_model = browser_->tab_strip_model();
    232   int count = tab_model->count();
    233   for (int index = 0; index < count; ++index) {
    234     content::WebContents* contents = tab_model->GetWebContentsAt(index);
    235     if (!contents)
    236       continue;
    237 
    238     // Send new search URLs to the renderer.
    239     content::RenderProcessHost* rph = contents->GetRenderProcessHost();
    240     instant_service->SendSearchURLsToRenderer(rph);
    241 
    242     // Reload the contents to ensure that it gets assigned to a non-priviledged
    243     // renderer.
    244     if (!instant_service->IsInstantProcess(rph->GetID()))
    245       continue;
    246     contents->GetController().Reload(false);
    247   }
    248 }
    249