• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetInstantSearchPrerenderer(Profile * profile)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 
BrowserInstantController(Browser * browser)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 
~BrowserInstantController()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 
MaybeSwapInInstantNTPContents(const GURL & url,content::WebContents * source_contents,content::WebContents ** target_contents)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 
OpenInstant(WindowOpenDisposition disposition,const GURL & url)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 
profile() const155 Profile* BrowserInstantController::profile() const {
156   return browser_->profile();
157 }
158 
ReplaceWebContentsAt(int index,scoped_ptr<content::WebContents> new_contents)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 
GetActiveWebContents() const169 content::WebContents* BrowserInstantController::GetActiveWebContents() const {
170   return browser_->tab_strip_model()->GetActiveWebContents();
171 }
172 
ActiveTabChanged()173 void BrowserInstantController::ActiveTabChanged() {
174   instant_.ActiveTabChanged();
175 }
176 
TabDeactivated(content::WebContents * contents)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 
SetOmniboxBounds(const gfx::Rect & bounds)186 void BrowserInstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
187   instant_.SetOmniboxBounds(bounds);
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 // BrowserInstantController, SearchModelObserver implementation:
192 
ModelChanged(const SearchModel::State & old_state,const SearchModel::State & new_state)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 
DefaultSearchProviderChanged()217 void BrowserInstantController::DefaultSearchProviderChanged() {
218   ReloadTabsInInstantProcess();
219 }
220 
GoogleURLUpdated()221 void BrowserInstantController::GoogleURLUpdated() {
222   ReloadTabsInInstantProcess();
223 }
224 
ReloadTabsInInstantProcess()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