1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/search/instant_search_prerenderer.h"
#include <utility>
#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/search/search_tab_helper.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/search/search.h"
#include "components/search_engines/template_url_service.h"
namespace {
// Returns true if |match| is associated with the default search provider.
bool MatchIsFromDefaultSearchProvider(const AutocompleteMatch& match,
Profile* profile) {
DCHECK(profile);
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
return match.GetTemplateURL(template_url_service, false) ==
template_url_service->GetDefaultSearchProvider();
}
} // namespace
InstantSearchPrerenderer::InstantSearchPrerenderer(Profile* profile,
const GURL& prerender_url)
: profile_(profile), prerender_url_(prerender_url) {}
InstantSearchPrerenderer::~InstantSearchPrerenderer() {
if (prerender_handle_)
prerender_handle_->OnCancel();
}
// static
InstantSearchPrerenderer* InstantSearchPrerenderer::GetForProfile(
Profile* profile) {
DCHECK(profile);
InstantService* instant_service =
InstantServiceFactory::GetForProfile(profile);
return instant_service ? instant_service->GetInstantSearchPrerenderer()
: nullptr;
}
void InstantSearchPrerenderer::Init(
content::SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size) {
// TODO(kmadhusu): Enable Instant for Incognito profile.
if (profile_->IsOffTheRecord())
return;
// Only cancel the old prerender after starting the new one, so if the URLs
// are the same, the underlying prerender will be reused.
std::unique_ptr<prerender::PrerenderHandle> old_prerender_handle =
std::move(prerender_handle_);
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForBrowserContext(profile_);
if (prerender_manager) {
prerender_handle_ = prerender_manager->AddPrerenderForInstant(
prerender_url_, session_storage_namespace, size);
}
if (old_prerender_handle)
old_prerender_handle->OnCancel();
}
void InstantSearchPrerenderer::Cancel() {
if (!prerender_handle_)
return;
last_instant_suggestion_ = InstantSuggestion();
prerender_handle_->OnCancel();
prerender_handle_.reset();
}
void InstantSearchPrerenderer::Prerender(const InstantSuggestion& suggestion) {
if (!prerender_handle_)
return;
if (last_instant_suggestion_.text == suggestion.text)
return;
if (last_instant_suggestion_.text.empty() &&
!prerender_handle_->IsFinishedLoading())
return;
if (!prerender_contents())
return;
last_instant_suggestion_ = suggestion;
SearchTabHelper::FromWebContents(prerender_contents())->
SetSuggestionToPrefetch(suggestion);
}
void InstantSearchPrerenderer::Commit(
const base::string16& query,
const EmbeddedSearchRequestParams& params) {
DCHECK(prerender_handle_);
DCHECK(prerender_contents());
SearchTabHelper::FromWebContents(prerender_contents())->Submit(query, params);
}
bool InstantSearchPrerenderer::CanCommitQuery(
content::WebContents* source,
const base::string16& query) const {
if (!source || query.empty() || !prerender_handle_ ||
!prerender_handle_->IsFinishedLoading() ||
!prerender_contents() || !QueryMatchesPrefetch(query)) {
return false;
}
return true;
}
bool InstantSearchPrerenderer::UsePrerenderedPage(
const GURL& url,
chrome::NavigateParams* params) {
base::string16 search_terms =
search::ExtractSearchTermsFromURL(profile_, url);
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForBrowserContext(profile_);
if (search_terms.empty() || !params->target_contents ||
!prerender_contents() || !prerender_manager ||
!QueryMatchesPrefetch(search_terms) ||
params->disposition != WindowOpenDisposition::CURRENT_TAB) {
Cancel();
return false;
}
// Do not use prerendered page for renderer initiated search requests.
if (params->is_renderer_initiated &&
ui::PageTransitionCoreTypeIs(params->transition,
ui::PAGE_TRANSITION_LINK)) {
Cancel();
return false;
}
bool success = prerender_manager->MaybeUsePrerenderedPage(
prerender_contents()->GetURL(), params);
prerender_handle_.reset();
return success;
}
bool InstantSearchPrerenderer::IsAllowed(const AutocompleteMatch& match,
content::WebContents* source) const {
// We block prerendering for anything but search-type matches associated with
// the default search provider.
//
// This is more restrictive than necessary. All that's really needed to be
// able to successfully prerender is that the |destination_url| of |match| be
// from the same origin and path as the default search engine, and the params
// to be sent to the server be a subset of the params we can pass to the
// prerenderer. So for example, if we normally prerender search URLs like
// https://google.com/search?q=foo&x=bar, then any match with a URL like that,
// potentially with the q and/or x params omitted, is prerenderable.
//
// However, if the URL has other params _not_ normally in the prerendered URL,
// there's no way to pass them to the prerendered page, and worse, if the URL
// does something like specifying params in both the query and ref sections of
// the URL (as Google URLs often do), it can quickly become impossible to
// figure out how to correctly tease out the right param names and values to
// send. Rather than try and write parsing code to deal with all these kinds
// of cases, for various different search engines, including accommodating
// changing behavior over time, we do the simple restriction described above.
// This handles the by-far-the-most-common cases while still being simple and
// maintainable.
return source && AutocompleteMatch::IsSearchType(match.type) &&
MatchIsFromDefaultSearchProvider(match, profile_);
}
content::WebContents* InstantSearchPrerenderer::prerender_contents() const {
return (prerender_handle_ && prerender_handle_->contents()) ?
prerender_handle_->contents()->prerender_contents() : NULL;
}
bool InstantSearchPrerenderer::QueryMatchesPrefetch(
const base::string16& query) const {
return search::ShouldReuseInstantSearchBasePage() ||
last_instant_suggestion_.text == query;
}
|