blob: c36cad8f18c4a95cdad1b152c92d0a5434947b9f [file] [log] [blame]
[email protected]e41982a72012-11-20 07:16:511// Copyright 2012 The Chromium Authors. All rights reserved.
[email protected]7acfaf92012-07-11 15:51:592// 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
[email protected]0a46856e2013-04-24 00:33:027#include "base/bind.h"
[email protected]3853a4c2013-02-11 17:15:578#include "base/prefs/pref_service.h"
[email protected]7acfaf92012-07-11 15:51:599#include "chrome/browser/extensions/extension_service.h"
[email protected]26015ac2013-02-27 02:50:1310#include "chrome/browser/extensions/extension_web_ui.h"
[email protected]7acfaf92012-07-11 15:51:5911#include "chrome/browser/profiles/profile.h"
[email protected]0a46856e2013-04-24 00:33:0212#include "chrome/browser/search/instant_service.h"
13#include "chrome/browser/search/instant_service_factory.h"
[email protected]a7b8e43d2013-03-18 18:52:4314#include "chrome/browser/search/search.h"
[email protected]0a46856e2013-04-24 00:33:0215#include "chrome/browser/search_engines/template_url.h"
16#include "chrome/browser/search_engines/template_url_service.h"
17#include "chrome/browser/search_engines/template_url_service_factory.h"
[email protected]7acfaf92012-07-11 15:51:5918#include "chrome/browser/ui/browser.h"
[email protected]7acfaf92012-07-11 15:51:5919#include "chrome/browser/ui/browser_window.h"
20#include "chrome/browser/ui/omnibox/location_bar.h"
[email protected]c18cb672012-12-05 04:42:1221#include "chrome/browser/ui/omnibox/omnibox_view.h"
[email protected]4ff347e2013-07-22 19:39:0022#include "chrome/browser/ui/search/instant_ntp.h"
[email protected]4066a695d2013-06-20 14:08:5423#include "chrome/browser/ui/search/search_model.h"
[email protected]9d3d11702012-11-08 01:01:1224#include "chrome/browser/ui/search/search_tab_helper.h"
[email protected]e41982a72012-11-20 07:16:5125#include "chrome/browser/ui/tabs/tab_strip_model.h"
[email protected]7acfaf92012-07-11 15:51:5926#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
[email protected]7acfaf92012-07-11 15:51:5927#include "chrome/common/pref_names.h"
[email protected]0c9406632013-02-08 01:13:3328#include "chrome/common/url_constants.h"
[email protected]75fee372013-03-06 00:42:4429#include "components/user_prefs/pref_registry_syncable.h"
[email protected]0a46856e2013-04-24 00:33:0230#include "content/public/browser/render_process_host.h"
[email protected]233f0f962013-02-27 21:14:1931#include "content/public/browser/user_metrics.h"
[email protected]0a46856e2013-04-24 00:33:0232#include "content/public/browser/web_contents.h"
[email protected]a6827652012-11-20 23:41:0833
[email protected]233f0f962013-02-27 21:14:1934using content::UserMetricsAction;
35
[email protected]7acfaf92012-07-11 15:51:5936////////////////////////////////////////////////////////////////////////////////
37// BrowserInstantController, public:
38
39BrowserInstantController::BrowserInstantController(Browser* browser)
[email protected]8a236702012-09-28 13:30:5740 : browser_(browser),
[email protected]d58688c62013-07-03 23:09:1241 instant_(this, chrome::IsInstantExtendedAPIEnabled()),
[email protected]6af41782013-06-22 13:49:1142 instant_unload_handler_(browser) {
[email protected]5fac3782013-03-06 09:32:3143 profile_pref_registrar_.Init(profile()->GetPrefs());
[email protected]0b3fa502012-11-21 13:57:4644 profile_pref_registrar_.Add(
[email protected]0a46856e2013-04-24 00:33:0245 prefs::kDefaultSearchProviderID,
46 base::Bind(&BrowserInstantController::OnDefaultSearchProviderChanged,
47 base::Unretained(this)));
[email protected]0b10c9ff2012-10-09 17:31:5548 browser_->search_model()->AddObserver(this);
[email protected]4ff347e2013-07-22 19:39:0049
50 InstantService* instant_service =
51 InstantServiceFactory::GetForProfile(profile());
52 instant_service->OnBrowserInstantControllerCreated();
[email protected]7acfaf92012-07-11 15:51:5953}
54
55BrowserInstantController::~BrowserInstantController() {
[email protected]0b10c9ff2012-10-09 17:31:5556 browser_->search_model()->RemoveObserver(this);
[email protected]4ff347e2013-07-22 19:39:0057
58 InstantService* instant_service =
59 InstantServiceFactory::GetForProfile(profile());
60 instant_service->OnBrowserInstantControllerDestroyed();
[email protected]7acfaf92012-07-11 15:51:5961}
62
[email protected]0c9406632013-02-08 01:13:3363bool BrowserInstantController::MaybeSwapInInstantNTPContents(
64 const GURL& url,
65 content::WebContents* source_contents,
66 content::WebContents** target_contents) {
67 if (url != GURL(chrome::kChromeUINewTabURL))
68 return false;
69
[email protected]26015ac2013-02-27 02:50:1370 GURL extension_url(url);
71 if (ExtensionWebUI::HandleChromeURLOverride(&extension_url, profile())) {
72 // If there is an extension overriding the NTP do not use the Instant NTP.
73 return false;
74 }
75
[email protected]4ff347e2013-07-22 19:39:0076 InstantService* instant_service =
77 InstantServiceFactory::GetForProfile(profile());
78 scoped_ptr<content::WebContents> instant_ntp =
79 instant_service->ReleaseNTPContents();
[email protected]0c9406632013-02-08 01:13:3380 if (!instant_ntp)
81 return false;
82
83 *target_contents = instant_ntp.get();
[email protected]0c9406632013-02-08 01:13:3384 if (source_contents) {
[email protected]474f8512013-05-31 22:31:1685 // If the Instant NTP hasn't yet committed an entry, we can't call
86 // CopyStateFromAndPrune. Instead, load the Local NTP URL directly in the
87 // source contents.
88 // TODO(sreeram): Always using the local URL is wrong in the case of the
89 // first tab in a window where we might want to use the remote URL. Fix.
90 if (!instant_ntp->GetController().CanPruneAllButVisible()) {
91 source_contents->GetController().LoadURL(chrome::GetLocalInstantURL(
92 profile()), content::Referrer(), content::PAGE_TRANSITION_GENERATED,
93 std::string());
94 *target_contents = source_contents;
95 } else {
96 instant_ntp->GetController().CopyStateFromAndPrune(
97 &source_contents->GetController());
98 ReplaceWebContentsAt(
99 browser_->tab_strip_model()->GetIndexOfWebContents(source_contents),
100 instant_ntp.Pass());
101 }
[email protected]0c9406632013-02-08 01:13:33102 } else {
[email protected]474f8512013-05-31 22:31:16103 // If the Instant NTP hasn't yet committed an entry, we can't call
104 // PruneAllButVisible. In that case, there shouldn't be any entries to
105 // prune anyway.
106 if (instant_ntp->GetController().CanPruneAllButVisible())
107 instant_ntp->GetController().PruneAllButVisible();
108 else
109 CHECK(!instant_ntp->GetController().GetLastCommittedEntry());
110
[email protected]0c9406632013-02-08 01:13:33111 // If |source_contents| is NULL, then the caller is responsible for
112 // inserting instant_ntp into the tabstrip and will take ownership.
113 ignore_result(instant_ntp.release());
114 }
[email protected]0c9406632013-02-08 01:13:33115 return true;
116}
117
[email protected]413558cb2013-06-10 16:44:45118bool BrowserInstantController::OpenInstant(WindowOpenDisposition disposition,
119 const GURL& url) {
[email protected]e41982a72012-11-20 07:16:51120 // Unsupported dispositions.
[email protected]413558cb2013-06-10 16:44:45121 if (disposition == NEW_BACKGROUND_TAB || disposition == NEW_WINDOW ||
122 disposition == NEW_FOREGROUND_TAB)
[email protected]7acfaf92012-07-11 15:51:59123 return false;
[email protected]7acfaf92012-07-11 15:51:59124
[email protected]7acfaf92012-07-11 15:51:59125 // The omnibox currently doesn't use other dispositions, so we don't attempt
[email protected]c72226c82012-10-01 21:02:32126 // to handle them. If you hit this DCHECK file a bug and I'll (sky) add
[email protected]7acfaf92012-07-11 15:51:59127 // support for the new disposition.
[email protected]413558cb2013-06-10 16:44:45128 DCHECK(disposition == CURRENT_TAB) << disposition;
[email protected]c72226c82012-10-01 21:02:32129
[email protected]413558cb2013-06-10 16:44:45130 // If we will not be replacing search terms from this URL, don't send to
131 // InstantController.
132 const string16& search_terms =
133 chrome::GetSearchTermsFromURL(browser_->profile(), url);
134 if (search_terms.empty())
135 return false;
136
137 return instant_.SubmitQuery(search_terms);
[email protected]7acfaf92012-07-11 15:51:59138}
139
[email protected]0c9406632013-02-08 01:13:33140Profile* BrowserInstantController::profile() const {
141 return browser_->profile();
142}
143
[email protected]0c9406632013-02-08 01:13:33144void BrowserInstantController::ReplaceWebContentsAt(
145 int index,
146 scoped_ptr<content::WebContents> new_contents) {
147 DCHECK_NE(TabStripModel::kNoTab, index);
[email protected]d572bfd2013-02-14 06:14:20148 scoped_ptr<content::WebContents> old_contents(browser_->tab_strip_model()->
149 ReplaceWebContentsAt(index, new_contents.release()));
150 instant_unload_handler_.RunUnloadListenersOrDestroy(old_contents.Pass(),
151 index);
[email protected]0c9406632013-02-08 01:13:33152}
153
[email protected]10aec592013-03-06 20:19:00154void BrowserInstantController::FocusOmnibox(bool caret_visibility) {
[email protected]c18cb672012-12-05 04:42:12155 OmniboxView* omnibox_view = browser_->window()->GetLocationBar()->
156 GetLocationEntry();
157 omnibox_view->SetFocus();
[email protected]10aec592013-03-06 20:19:00158 omnibox_view->model()->SetCaretVisibility(caret_visibility);
[email protected]0d0b4a42013-06-14 00:46:26159 if (!caret_visibility) {
160 // If the user clicked on the fakebox, any text already in the omnibox
161 // should get cleared when they start typing. Selecting all the existing
162 // text is a convenient way to accomplish this. It also gives a slight
163 // visual cue to users who really understand selection state about what will
164 // happen if they start typing.
165 omnibox_view->SelectAll(false);
166 }
[email protected]c18cb672012-12-05 04:42:12167}
168
[email protected]cd533bf2012-12-04 19:14:59169content::WebContents* BrowserInstantController::GetActiveWebContents() const {
170 return browser_->tab_strip_model()->GetActiveWebContents();
[email protected]7acfaf92012-07-11 15:51:59171}
172
[email protected]e41982a72012-11-20 07:16:51173void BrowserInstantController::ActiveTabChanged() {
174 instant_.ActiveTabChanged();
175}
176
[email protected]3d6a8952012-12-14 03:18:07177void BrowserInstantController::TabDeactivated(content::WebContents* contents) {
178 instant_.TabDeactivated(contents);
179}
180
[email protected]3c3acca2013-02-26 03:07:07181void BrowserInstantController::OpenURL(
[email protected]e3033eb2012-12-13 23:46:08182 const GURL& url,
[email protected]3c3acca2013-02-26 03:07:07183 content::PageTransition transition,
184 WindowOpenDisposition disposition) {
[email protected]e3033eb2012-12-13 23:46:08185 browser_->OpenURL(content::OpenURLParams(url,
186 content::Referrer(),
[email protected]3c3acca2013-02-26 03:07:07187 disposition,
[email protected]e3033eb2012-12-13 23:46:08188 transition,
189 false));
190}
191
[email protected]fcde79a2013-02-28 02:25:09192void BrowserInstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
193 instant_.SetOmniboxBounds(bounds);
[email protected]ec4aad542012-12-14 01:11:04194}
195
[email protected]3473ae02013-06-07 00:28:08196void BrowserInstantController::ToggleVoiceSearch() {
197 instant_.ToggleVoiceSearch();
198}
199
[email protected]7acfaf92012-07-11 15:51:59200////////////////////////////////////////////////////////////////////////////////
[email protected]165fe422013-03-27 06:34:03201// BrowserInstantController, SearchModelObserver implementation:
[email protected]0b10c9ff2012-10-09 17:31:55202
[email protected]5ee671f2013-03-19 11:23:05203void BrowserInstantController::ModelChanged(
[email protected]165fe422013-03-27 06:34:03204 const SearchModel::State& old_state,
205 const SearchModel::State& new_state) {
[email protected]4066a695d2013-06-20 14:08:54206 if (old_state.mode != new_state.mode) {
207 const SearchMode& new_mode = new_state.mode;
[email protected]5ee671f2013-03-19 11:23:05208
[email protected]4066a695d2013-06-20 14:08:54209 if (chrome::IsInstantExtendedAPIEnabled()) {
210 // Record some actions corresponding to the mode change. Note that to get
211 // the full story, it's necessary to look at other UMA actions as well,
212 // such as tab switches.
213 if (new_mode.is_search_results())
214 content::RecordAction(UserMetricsAction("InstantExtended.ShowSRP"));
215 else if (new_mode.is_ntp())
216 content::RecordAction(UserMetricsAction("InstantExtended.ShowNTP"));
217 }
[email protected]5ee671f2013-03-19 11:23:05218
[email protected]4066a695d2013-06-20 14:08:54219 instant_.SearchModeChanged(old_state.mode, new_mode);
[email protected]c19ba1042013-03-11 17:17:13220 }
221
[email protected]4066a695d2013-06-20 14:08:54222 if (old_state.instant_support != new_state.instant_support)
223 instant_.InstantSupportChanged(new_state.instant_support);
[email protected]7acfaf92012-07-11 15:51:59224}
225
[email protected]0a46856e2013-04-24 00:33:02226void BrowserInstantController::OnDefaultSearchProviderChanged(
227 const std::string& pref_name) {
228 DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID));
229
230 Profile* browser_profile = profile();
231 const TemplateURL* template_url =
232 TemplateURLServiceFactory::GetForProfile(browser_profile)->
233 GetDefaultSearchProvider();
234 if (!template_url) {
235 // A NULL |template_url| could mean either this notification is sent during
236 // the browser start up operation or the user now has no default search
237 // provider. There is no way for the user to reach this state using the
238 // Chrome settings. Only explicitly poking at the DB or bugs in the Sync
239 // could cause that, neither of which we support.
240 return;
241 }
242
243 InstantService* instant_service =
244 InstantServiceFactory::GetForProfile(browser_profile);
245 if (!instant_service)
246 return;
247
248 TabStripModel* tab_model = browser_->tab_strip_model();
249 int count = tab_model->count();
250 for (int index = 0; index < count; ++index) {
251 content::WebContents* contents = tab_model->GetWebContentsAt(index);
252 if (!contents)
253 continue;
254
[email protected]0a46856e2013-04-24 00:33:02255 if (!instant_service->IsInstantProcess(
256 contents->GetRenderProcessHost()->GetID()))
257 continue;
258
259 // Reload the contents to ensure that it gets assigned to a non-priviledged
260 // renderer.
261 contents->GetController().Reload(false);
262 }
[email protected]0a46856e2013-04-24 00:33:02263}