Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / android / omnibox / autocomplete_controller_android.cc
blob7a85f8ab27b2642e8aa932ab0406d6b7138f8b4e
1 // Copyright 2014 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/android/omnibox/autocomplete_controller_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 #include "base/timer/timer.h"
14 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
15 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
16 #include "chrome/browser/autocomplete/autocomplete_controller.h"
17 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
18 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
19 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/omnibox/omnibox_log.h"
23 #include "chrome/browser/profiles/incognito_helpers.h"
24 #include "chrome/browser/profiles/profile_android.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/browser/search/search.h"
27 #include "chrome/browser/search_engines/template_url_service_factory.h"
28 #include "chrome/browser/sessions/session_tab_helper.h"
29 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
30 #include "chrome/browser/ui/toolbar/toolbar_model.h"
31 #include "chrome/common/instant_types.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/url_constants.h"
34 #include "components/bookmarks/browser/bookmark_model.h"
35 #include "components/keyed_service/content/browser_context_dependency_manager.h"
36 #include "components/metrics/proto/omnibox_event.pb.h"
37 #include "components/omnibox/autocomplete_input.h"
38 #include "components/omnibox/autocomplete_match.h"
39 #include "components/omnibox/autocomplete_match_type.h"
40 #include "components/omnibox/omnibox_field_trial.h"
41 #include "components/omnibox/search_provider.h"
42 #include "components/search/search.h"
43 #include "components/search_engines/template_url_service.h"
44 #include "content/public/browser/notification_details.h"
45 #include "content/public/browser/notification_service.h"
46 #include "content/public/browser/notification_source.h"
47 #include "content/public/browser/web_contents.h"
48 #include "content/public/common/url_constants.h"
49 #include "jni/AutocompleteController_jni.h"
50 #include "net/base/escape.h"
51 #include "net/base/net_util.h"
52 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
54 using base::android::AttachCurrentThread;
55 using base::android::ConvertJavaStringToUTF16;
56 using base::android::ConvertUTF8ToJavaString;
57 using base::android::ConvertUTF16ToJavaString;
58 using bookmarks::BookmarkModel;
59 using metrics::OmniboxEventProto;
61 namespace {
63 const int kAndroidAutocompleteProviders =
64 AutocompleteClassifier::kDefaultOmniboxProviders;
66 /**
67 * A prefetcher class responsible for triggering zero suggest prefetch.
68 * The prefetch occurs as a side-effect of calling OnOmniboxFocused() on
69 * the AutocompleteController object.
71 class ZeroSuggestPrefetcher : public AutocompleteControllerDelegate {
72 public:
73 explicit ZeroSuggestPrefetcher(Profile* profile);
75 private:
76 ~ZeroSuggestPrefetcher() override;
77 void SelfDestruct();
79 // AutocompleteControllerDelegate:
80 void OnResultChanged(bool default_match_changed) override;
82 scoped_ptr<AutocompleteController> controller_;
83 base::OneShotTimer<ZeroSuggestPrefetcher> expire_timer_;
86 ZeroSuggestPrefetcher::ZeroSuggestPrefetcher(Profile* profile)
87 : controller_(new AutocompleteController(
88 profile, TemplateURLServiceFactory::GetForProfile(profile), this,
89 AutocompleteProvider::TYPE_ZERO_SUGGEST)) {
90 // Creating an arbitrary fake_request_source to avoid passing in an invalid
91 // AutocompleteInput object.
92 base::string16 fake_request_source(base::ASCIIToUTF16(
93 "http://www.foobarbazblah.com"));
94 controller_->OnOmniboxFocused(AutocompleteInput(
95 fake_request_source, base::string16::npos, std::string(),
96 GURL(fake_request_source), OmniboxEventProto::INVALID_SPEC, false, false,
97 true, true, ChromeAutocompleteSchemeClassifier(profile)));
98 // Delete ourselves after 10s. This is enough time to cache results or
99 // give up if the results haven't been received.
100 expire_timer_.Start(FROM_HERE,
101 base::TimeDelta::FromMilliseconds(10000),
102 this, &ZeroSuggestPrefetcher::SelfDestruct);
105 ZeroSuggestPrefetcher::~ZeroSuggestPrefetcher() {
108 void ZeroSuggestPrefetcher::SelfDestruct() {
109 delete this;
112 void ZeroSuggestPrefetcher::OnResultChanged(bool default_match_changed) {
113 // Nothing to do here, the results have been cached.
114 // We don't want to trigger deletion here because this is being called by the
115 // AutocompleteController object.
118 } // namespace
120 AutocompleteControllerAndroid::AutocompleteControllerAndroid(Profile* profile)
121 : autocomplete_controller_(new AutocompleteController(
122 profile, TemplateURLServiceFactory::GetForProfile(profile), this,
123 kAndroidAutocompleteProviders)),
124 inside_synchronous_start_(false),
125 profile_(profile) {
128 void AutocompleteControllerAndroid::Start(JNIEnv* env,
129 jobject obj,
130 jstring j_text,
131 jint j_cursor_pos,
132 jstring j_desired_tld,
133 jstring j_current_url,
134 bool prevent_inline_autocomplete,
135 bool prefer_keyword,
136 bool allow_exact_keyword_match,
137 bool want_asynchronous_matches) {
138 if (!autocomplete_controller_)
139 return;
141 std::string desired_tld;
142 GURL current_url;
143 if (j_current_url != NULL)
144 current_url = GURL(ConvertJavaStringToUTF16(env, j_current_url));
145 if (j_desired_tld != NULL)
146 desired_tld = base::android::ConvertJavaStringToUTF8(env, j_desired_tld);
147 base::string16 text = ConvertJavaStringToUTF16(env, j_text);
148 OmniboxEventProto::PageClassification page_classification =
149 OmniboxEventProto::OTHER;
150 size_t cursor_pos = j_cursor_pos == -1 ? base::string16::npos : j_cursor_pos;
151 input_ = AutocompleteInput(
152 text, cursor_pos, desired_tld, current_url, page_classification,
153 prevent_inline_autocomplete, prefer_keyword, allow_exact_keyword_match,
154 want_asynchronous_matches, ChromeAutocompleteSchemeClassifier(profile_));
155 autocomplete_controller_->Start(input_);
158 ScopedJavaLocalRef<jobject> AutocompleteControllerAndroid::Classify(
159 JNIEnv* env,
160 jobject obj,
161 jstring j_text) {
162 return GetTopSynchronousResult(env, obj, j_text, true);
165 void AutocompleteControllerAndroid::OnOmniboxFocused(
166 JNIEnv* env,
167 jobject obj,
168 jstring j_omnibox_text,
169 jstring j_current_url,
170 jboolean is_query_in_omnibox,
171 jboolean focused_from_fakebox) {
172 if (!autocomplete_controller_)
173 return;
175 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
176 const GURL current_url = GURL(url);
177 base::string16 omnibox_text = ConvertJavaStringToUTF16(env, j_omnibox_text);
179 // If omnibox text is empty, set it to the current URL for the purposes of
180 // populating the verbatim match.
181 if (omnibox_text.empty())
182 omnibox_text = url;
184 input_ = AutocompleteInput(
185 omnibox_text, base::string16::npos, std::string(), current_url,
186 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox),
187 false, false, true, true, ChromeAutocompleteSchemeClassifier(profile_));
188 autocomplete_controller_->OnOmniboxFocused(input_);
191 void AutocompleteControllerAndroid::Stop(JNIEnv* env,
192 jobject obj,
193 bool clear_results) {
194 if (autocomplete_controller_ != NULL)
195 autocomplete_controller_->Stop(clear_results);
198 void AutocompleteControllerAndroid::ResetSession(JNIEnv* env, jobject obj) {
199 if (autocomplete_controller_ != NULL)
200 autocomplete_controller_->ResetSession();
203 void AutocompleteControllerAndroid::OnSuggestionSelected(
204 JNIEnv* env,
205 jobject obj,
206 jint selected_index,
207 jstring j_current_url,
208 jboolean is_query_in_omnibox,
209 jboolean focused_from_fakebox,
210 jlong elapsed_time_since_first_modified,
211 jobject j_web_contents) {
212 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
213 const GURL current_url = GURL(url);
214 OmniboxEventProto::PageClassification current_page_classification =
215 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox);
216 const base::TimeTicks& now(base::TimeTicks::Now());
217 content::WebContents* web_contents =
218 content::WebContents::FromJavaWebContents(j_web_contents);
220 OmniboxLog log(
221 input_.text(),
222 false, /* don't know */
223 input_.type(),
224 true,
225 selected_index,
226 false,
227 SessionTabHelper::IdForTab(web_contents),
228 current_page_classification,
229 base::TimeDelta::FromMilliseconds(elapsed_time_since_first_modified),
230 base::string16::npos,
231 now - autocomplete_controller_->last_time_default_match_changed(),
232 autocomplete_controller_->result());
233 autocomplete_controller_->AddProvidersInfo(&log.providers_info);
235 content::NotificationService::current()->Notify(
236 chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
237 content::Source<Profile>(profile_),
238 content::Details<OmniboxLog>(&log));
241 void AutocompleteControllerAndroid::DeleteSuggestion(JNIEnv* env,
242 jobject obj,
243 int selected_index) {
244 const AutocompleteResult& result = autocomplete_controller_->result();
245 const AutocompleteMatch& match = result.match_at(selected_index);
246 if (match.SupportsDeletion())
247 autocomplete_controller_->DeleteMatch(match);
250 ScopedJavaLocalRef<jstring> AutocompleteControllerAndroid::
251 UpdateMatchDestinationURLWithQueryFormulationTime(
252 JNIEnv* env,
253 jobject obj,
254 jint selected_index,
255 jlong elapsed_time_since_input_change) {
256 // In rare cases, we navigate to cached matches and the underlying result
257 // has already been cleared, in that case ignore the URL update.
258 if (autocomplete_controller_->result().empty())
259 return ScopedJavaLocalRef<jstring>();
261 AutocompleteMatch match(
262 autocomplete_controller_->result().match_at(selected_index));
263 autocomplete_controller_->UpdateMatchDestinationURLWithQueryFormulationTime(
264 base::TimeDelta::FromMilliseconds(elapsed_time_since_input_change),
265 &match);
266 return ConvertUTF8ToJavaString(env, match.destination_url.spec());
269 void AutocompleteControllerAndroid::Shutdown() {
270 autocomplete_controller_.reset();
272 JNIEnv* env = AttachCurrentThread();
273 ScopedJavaLocalRef<jobject> java_bridge =
274 weak_java_autocomplete_controller_android_.get(env);
275 if (java_bridge.obj())
276 Java_AutocompleteController_notifyNativeDestroyed(env, java_bridge.obj());
278 weak_java_autocomplete_controller_android_.reset();
281 // static
282 AutocompleteControllerAndroid*
283 AutocompleteControllerAndroid::Factory::GetForProfile(
284 Profile* profile, JNIEnv* env, jobject obj) {
285 AutocompleteControllerAndroid* bridge =
286 static_cast<AutocompleteControllerAndroid*>(
287 GetInstance()->GetServiceForBrowserContext(profile, true));
288 bridge->InitJNI(env, obj);
289 return bridge;
292 AutocompleteControllerAndroid::Factory*
293 AutocompleteControllerAndroid::Factory::GetInstance() {
294 return Singleton<AutocompleteControllerAndroid::Factory>::get();
297 content::BrowserContext*
298 AutocompleteControllerAndroid::Factory::GetBrowserContextToUse(
299 content::BrowserContext* context) const {
300 return chrome::GetBrowserContextOwnInstanceInIncognito(context);
303 AutocompleteControllerAndroid::Factory::Factory()
304 : BrowserContextKeyedServiceFactory(
305 "AutocompleteControllerAndroid",
306 BrowserContextDependencyManager::GetInstance()) {
307 DependsOn(ShortcutsBackendFactory::GetInstance());
310 AutocompleteControllerAndroid::Factory::~Factory() {
313 KeyedService* AutocompleteControllerAndroid::Factory::BuildServiceInstanceFor(
314 content::BrowserContext* profile) const {
315 return new AutocompleteControllerAndroid(static_cast<Profile*>(profile));
318 AutocompleteControllerAndroid::~AutocompleteControllerAndroid() {
321 void AutocompleteControllerAndroid::InitJNI(JNIEnv* env, jobject obj) {
322 weak_java_autocomplete_controller_android_ =
323 JavaObjectWeakGlobalRef(env, obj);
326 void AutocompleteControllerAndroid::OnResultChanged(
327 bool default_match_changed) {
328 if (!autocomplete_controller_)
329 return;
331 const AutocompleteResult& result = autocomplete_controller_->result();
332 const AutocompleteResult::const_iterator default_match(
333 result.default_match());
334 if ((default_match != result.end()) && default_match_changed &&
335 chrome::IsInstantExtendedAPIEnabled() &&
336 chrome::ShouldPrefetchSearchResults()) {
337 InstantSuggestion prefetch_suggestion;
338 // If the default match should be prefetched, do that.
339 if (SearchProvider::ShouldPrefetch(*default_match)) {
340 prefetch_suggestion.text = default_match->contents;
341 prefetch_suggestion.metadata =
342 SearchProvider::GetSuggestMetadata(*default_match);
344 // Send the prefetch suggestion unconditionally to the Instant search base
345 // page. If there is no suggestion to prefetch, we need to send a blank
346 // query to clear the prefetched results.
347 InstantSearchPrerenderer* prerenderer =
348 InstantSearchPrerenderer::GetForProfile(profile_);
349 if (prerenderer)
350 prerenderer->Prerender(prefetch_suggestion);
352 if (!inside_synchronous_start_)
353 NotifySuggestionsReceived(autocomplete_controller_->result());
356 void AutocompleteControllerAndroid::NotifySuggestionsReceived(
357 const AutocompleteResult& autocomplete_result) {
358 JNIEnv* env = AttachCurrentThread();
359 ScopedJavaLocalRef<jobject> java_bridge =
360 weak_java_autocomplete_controller_android_.get(env);
361 if (!java_bridge.obj())
362 return;
364 ScopedJavaLocalRef<jobject> suggestion_list_obj =
365 Java_AutocompleteController_createOmniboxSuggestionList(
366 env, autocomplete_result.size());
367 for (size_t i = 0; i < autocomplete_result.size(); ++i) {
368 ScopedJavaLocalRef<jobject> j_omnibox_suggestion =
369 BuildOmniboxSuggestion(env, autocomplete_result.match_at(i));
370 Java_AutocompleteController_addOmniboxSuggestionToList(
371 env, suggestion_list_obj.obj(), j_omnibox_suggestion.obj());
374 // Get the inline-autocomplete text.
375 const AutocompleteResult::const_iterator default_match(
376 autocomplete_result.default_match());
377 base::string16 inline_autocomplete_text;
378 if (default_match != autocomplete_result.end()) {
379 inline_autocomplete_text = default_match->inline_autocompletion;
381 ScopedJavaLocalRef<jstring> inline_text =
382 ConvertUTF16ToJavaString(env, inline_autocomplete_text);
383 jlong j_autocomplete_result =
384 reinterpret_cast<intptr_t>(&(autocomplete_result));
385 Java_AutocompleteController_onSuggestionsReceived(env,
386 java_bridge.obj(),
387 suggestion_list_obj.obj(),
388 inline_text.obj(),
389 j_autocomplete_result);
392 OmniboxEventProto::PageClassification
393 AutocompleteControllerAndroid::ClassifyPage(const GURL& gurl,
394 bool is_query_in_omnibox,
395 bool focused_from_fakebox) const {
396 if (!gurl.is_valid())
397 return OmniboxEventProto::INVALID_SPEC;
399 const std::string& url = gurl.spec();
401 if (gurl.SchemeIs(content::kChromeUIScheme) &&
402 gurl.host() == chrome::kChromeUINewTabHost) {
403 return OmniboxEventProto::NTP;
406 if (url == chrome::kChromeUINativeNewTabURL) {
407 return focused_from_fakebox ?
408 OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS :
409 OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS;
412 if (url == url::kAboutBlankURL)
413 return OmniboxEventProto::BLANK;
415 if (url == profile_->GetPrefs()->GetString(prefs::kHomePage))
416 return OmniboxEventProto::HOME_PAGE;
418 if (is_query_in_omnibox)
419 return OmniboxEventProto::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT;
421 bool is_search_url = TemplateURLServiceFactory::GetForProfile(profile_)->
422 IsSearchResultsPageFromDefaultSearchProvider(gurl);
423 if (is_search_url)
424 return OmniboxEventProto::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT;
426 return OmniboxEventProto::OTHER;
429 ScopedJavaLocalRef<jobject>
430 AutocompleteControllerAndroid::BuildOmniboxSuggestion(
431 JNIEnv* env,
432 const AutocompleteMatch& match) {
433 ScopedJavaLocalRef<jstring> contents =
434 ConvertUTF16ToJavaString(env, match.contents);
435 ScopedJavaLocalRef<jstring> description =
436 ConvertUTF16ToJavaString(env, match.description);
437 ScopedJavaLocalRef<jstring> answer_contents =
438 ConvertUTF16ToJavaString(env, match.answer_contents);
439 ScopedJavaLocalRef<jstring> answer_type =
440 ConvertUTF16ToJavaString(env, match.answer_type);
441 ScopedJavaLocalRef<jstring> fill_into_edit =
442 ConvertUTF16ToJavaString(env, match.fill_into_edit);
443 ScopedJavaLocalRef<jstring> destination_url =
444 ConvertUTF8ToJavaString(env, match.destination_url.spec());
445 // Note that we are also removing 'www' host from formatted url.
446 ScopedJavaLocalRef<jstring> formatted_url = ConvertUTF16ToJavaString(env,
447 FormatURLUsingAcceptLanguages(match.stripped_destination_url));
448 BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile_);
449 return Java_AutocompleteController_buildOmniboxSuggestion(
450 env,
451 match.type,
452 match.relevance,
453 match.transition,
454 contents.obj(),
455 description.obj(),
456 answer_contents.obj(),
457 answer_type.obj(),
458 fill_into_edit.obj(),
459 destination_url.obj(),
460 formatted_url.obj(),
461 bookmark_model && bookmark_model->IsBookmarked(match.destination_url),
462 match.SupportsDeletion());
465 base::string16 AutocompleteControllerAndroid::FormatURLUsingAcceptLanguages(
466 GURL url) {
467 if (profile_ == NULL)
468 return base::string16();
470 std::string languages(
471 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
473 return net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
474 net::UnescapeRule::SPACES, NULL, NULL, NULL);
477 ScopedJavaLocalRef<jobject>
478 AutocompleteControllerAndroid::GetTopSynchronousResult(
479 JNIEnv* env,
480 jobject obj,
481 jstring j_text,
482 bool prevent_inline_autocomplete) {
483 if (!autocomplete_controller_)
484 return ScopedJavaLocalRef<jobject>();
486 inside_synchronous_start_ = true;
487 Start(env,
488 obj,
489 j_text,
491 NULL,
492 NULL,
493 prevent_inline_autocomplete,
494 false,
495 false,
496 false);
497 inside_synchronous_start_ = false;
498 DCHECK(autocomplete_controller_->done());
499 const AutocompleteResult& result = autocomplete_controller_->result();
500 if (result.empty())
501 return ScopedJavaLocalRef<jobject>();
503 return BuildOmniboxSuggestion(env, *result.begin());
506 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
507 Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
508 if (!profile)
509 return 0;
511 AutocompleteControllerAndroid* native_bridge =
512 AutocompleteControllerAndroid::Factory::GetForProfile(profile, env, obj);
513 return reinterpret_cast<intptr_t>(native_bridge);
516 static jstring QualifyPartialURLQuery(
517 JNIEnv* env, jclass clazz, jstring jquery) {
518 Profile* profile = ProfileManager::GetActiveUserProfile();
519 if (!profile)
520 return NULL;
521 AutocompleteMatch match;
522 base::string16 query_string(ConvertJavaStringToUTF16(env, jquery));
523 AutocompleteClassifierFactory::GetForProfile(profile)->Classify(
524 query_string,
525 false,
526 false,
527 OmniboxEventProto::INVALID_SPEC,
528 &match,
529 NULL);
530 if (!match.destination_url.is_valid())
531 return NULL;
533 // Only return a URL if the match is a URL type.
534 if (match.type != AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
535 match.type != AutocompleteMatchType::HISTORY_URL &&
536 match.type != AutocompleteMatchType::NAVSUGGEST)
537 return NULL;
539 // As we are returning to Java, it is fine to call Release().
540 return ConvertUTF8ToJavaString(env, match.destination_url.spec()).Release();
543 static void PrefetchZeroSuggestResults(JNIEnv* env, jclass clazz) {
544 Profile* profile = ProfileManager::GetActiveUserProfile();
545 if (!profile)
546 return;
548 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial())
549 return;
551 // ZeroSuggestPrefetcher deletes itself after it's done prefetching.
552 new ZeroSuggestPrefetcher(profile);
555 // Register native methods
556 bool RegisterAutocompleteControllerAndroid(JNIEnv* env) {
557 return RegisterNativesImpl(env);