1 // Copyright 2013 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.
5 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
7 #include "chrome/browser/prerender/prerender_handle.h"
8 #include "chrome/browser/prerender/prerender_manager.h"
9 #include "chrome/browser/prerender/prerender_manager_factory.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/search_engines/template_url_service_factory.h"
15 #include "chrome/browser/ui/browser_navigator.h"
16 #include "chrome/browser/ui/search/search_tab_helper.h"
17 #include "components/omnibox/browser/autocomplete_match.h"
18 #include "components/search/search.h"
19 #include "components/search_engines/template_url_service.h"
23 // Returns true if the underlying page supports Instant search.
24 bool PageSupportsInstantSearch(content::WebContents
* contents
) {
25 // Search results page supports Instant search.
26 return SearchTabHelper::FromWebContents(contents
)->IsSearchResultsPage();
29 // Returns true if |match| is associated with the default search provider.
30 bool MatchIsFromDefaultSearchProvider(const AutocompleteMatch
& match
,
33 TemplateURLService
* template_url_service
=
34 TemplateURLServiceFactory::GetForProfile(profile
);
35 return match
.GetTemplateURL(template_url_service
, false) ==
36 template_url_service
->GetDefaultSearchProvider();
41 InstantSearchPrerenderer::InstantSearchPrerenderer(Profile
* profile
,
47 InstantSearchPrerenderer::~InstantSearchPrerenderer() {
48 if (prerender_handle_
)
49 prerender_handle_
->OnCancel();
53 InstantSearchPrerenderer
* InstantSearchPrerenderer::GetForProfile(
56 InstantService
* instant_service
=
57 InstantServiceFactory::GetForProfile(profile
);
58 return instant_service
? instant_service
->instant_search_prerenderer() : NULL
;
61 void InstantSearchPrerenderer::Init(
62 content::SessionStorageNamespace
* session_storage_namespace
,
63 const gfx::Size
& size
) {
64 // TODO(kmadhusu): Enable Instant for Incognito profile.
65 if (profile_
->IsOffTheRecord())
68 // Only cancel the old prerender after starting the new one, so if the URLs
69 // are the same, the underlying prerender will be reused.
70 scoped_ptr
<prerender::PrerenderHandle
> old_prerender_handle(
71 prerender_handle_
.release());
72 prerender::PrerenderManager
* prerender_manager
=
73 prerender::PrerenderManagerFactory::GetForProfile(profile_
);
74 if (prerender_manager
) {
75 prerender_handle_
.reset(prerender_manager
->AddPrerenderForInstant(
76 prerender_url_
, session_storage_namespace
, size
));
78 if (old_prerender_handle
)
79 old_prerender_handle
->OnCancel();
82 void InstantSearchPrerenderer::Cancel() {
83 if (!prerender_handle_
)
86 last_instant_suggestion_
= InstantSuggestion();
87 prerender_handle_
->OnCancel();
88 prerender_handle_
.reset();
91 void InstantSearchPrerenderer::Prerender(const InstantSuggestion
& suggestion
) {
92 if (!prerender_handle_
)
95 if (last_instant_suggestion_
.text
== suggestion
.text
)
98 if (last_instant_suggestion_
.text
.empty() &&
99 !prerender_handle_
->IsFinishedLoading())
102 if (!prerender_contents())
105 last_instant_suggestion_
= suggestion
;
106 SearchTabHelper::FromWebContents(prerender_contents())->
107 SetSuggestionToPrefetch(suggestion
);
110 void InstantSearchPrerenderer::Commit(
111 const base::string16
& query
,
112 const EmbeddedSearchRequestParams
& params
) {
113 DCHECK(prerender_handle_
);
114 DCHECK(prerender_contents());
115 SearchTabHelper::FromWebContents(prerender_contents())->Submit(query
, params
);
118 bool InstantSearchPrerenderer::CanCommitQuery(
119 content::WebContents
* source
,
120 const base::string16
& query
) const {
121 if (!source
|| query
.empty() || !prerender_handle_
||
122 !prerender_handle_
->IsFinishedLoading() ||
123 !prerender_contents() || !QueryMatchesPrefetch(query
)) {
127 // InstantSearchPrerenderer can commit query to the prerendered page only if
128 // the underlying |source| page doesn't support Instant search.
129 return !PageSupportsInstantSearch(source
);
132 bool InstantSearchPrerenderer::UsePrerenderedPage(
134 chrome::NavigateParams
* params
) {
135 base::string16 search_terms
=
136 search::ExtractSearchTermsFromURL(profile_
, url
);
137 prerender::PrerenderManager
* prerender_manager
=
138 prerender::PrerenderManagerFactory::GetForProfile(profile_
);
139 if (search_terms
.empty() || !params
->target_contents
||
140 !prerender_contents() || !prerender_manager
||
141 !QueryMatchesPrefetch(search_terms
) ||
142 params
->disposition
!= CURRENT_TAB
) {
147 // Do not use prerendered page for renderer initiated search requests.
148 if (params
->is_renderer_initiated
&&
149 params
->transition
== ui::PAGE_TRANSITION_LINK
) {
154 bool success
= prerender_manager
->MaybeUsePrerenderedPage(
155 prerender_contents()->GetURL(), params
);
156 prerender_handle_
.reset();
160 bool InstantSearchPrerenderer::IsAllowed(const AutocompleteMatch
& match
,
161 content::WebContents
* source
) const {
162 // We block prerendering for anything but search-type matches associated with
163 // the default search provider.
165 // This is more restrictive than necessary. All that's really needed to be
166 // able to successfully prerender is that the |destination_url| of |match| be
167 // from the same origin and path as the default search engine, and the params
168 // to be sent to the server be a subset of the params we can pass to the
169 // prerenderer. So for example, if we normally prerender search URLs like
170 // https://google.com/search?q=foo&x=bar, then any match with a URL like that,
171 // potentially with the q and/or x params omitted, is prerenderable.
173 // However, if the URL has other params _not_ normally in the prerendered URL,
174 // there's no way to pass them to the prerendered page, and worse, if the URL
175 // does something like specifying params in both the query and ref sections of
176 // the URL (as Google URLs often do), it can quickly become impossible to
177 // figure out how to correctly tease out the right param names and values to
178 // send. Rather than try and write parsing code to deal with all these kinds
179 // of cases, for various different search engines, including accommodating
180 // changing behavior over time, we do the simple restriction described above.
181 // This handles the by-far-the-most-common cases while still being simple and
183 return source
&& AutocompleteMatch::IsSearchType(match
.type
) &&
184 MatchIsFromDefaultSearchProvider(match
, profile_
) &&
185 !PageSupportsInstantSearch(source
);
188 content::WebContents
* InstantSearchPrerenderer::prerender_contents() const {
189 return (prerender_handle_
&& prerender_handle_
->contents()) ?
190 prerender_handle_
->contents()->prerender_contents() : NULL
;
193 bool InstantSearchPrerenderer::QueryMatchesPrefetch(
194 const base::string16
& query
) const {
195 if (search::ShouldReuseInstantSearchBasePage())
197 return last_instant_suggestion_
.text
== query
;