Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: chrome/browser/android/omnibox/autocomplete_controller_android.cc

Issue 285633002: Upstream AutocompleteController and related classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Made requested changes Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/android/omnibox/autocomplete_controller_android.h"
6
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/autocomplete_input.h"
18 #include "chrome/browser/autocomplete/autocomplete_match.h"
19 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/omnibox/omnibox_field_trial.h"
23 #include "chrome/browser/omnibox/omnibox_log.h"
24 #include "chrome/browser/profiles/incognito_helpers.h"
25 #include "chrome/browser/profiles/profile_android.h"
26 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/browser/search_engines/template_url_service.h"
28 #include "chrome/browser/search_engines/template_url_service_factory.h"
29 #include "chrome/browser/sessions/session_id.h"
30 #include "chrome/browser/ui/toolbar/toolbar_model.h"
31 #include "chrome/common/autocomplete_match_type.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/url_constants.h"
34 #include "components/keyed_service/content/browser_context_dependency_manager.h"
35 #include "content/public/browser/notification_details.h"
36 #include "content/public/browser/notification_service.h"
37 #include "content/public/browser/notification_source.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/common/url_constants.h"
40 #include "jni/AutocompleteController_jni.h"
41 #include "net/base/escape.h"
42 #include "net/base/net_util.h"
43 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
44 #include "ui/base/resource/resource_bundle.h"
45
46 using base::android::AttachCurrentThread;
47 using base::android::ConvertJavaStringToUTF16;
48 using base::android::ConvertUTF8ToJavaString;
49 using base::android::ConvertUTF16ToJavaString;
50
51 namespace {
52
53 const int kAndroidAutocompleteProviders =
54 AutocompleteClassifier::kDefaultOmniboxProviders;
55
56 /**
57 * A prefetcher class responsible for triggering zero suggest prefetch.
58 * The prefetch occurs as a side-effect of calling StartZeroSuggest() on
59 * the AutocompleteController object.
60 */
61 class ZeroSuggestPrefetcher : public AutocompleteControllerDelegate {
62 public:
63 explicit ZeroSuggestPrefetcher(Profile* profile);
64
65 private:
66 virtual ~ZeroSuggestPrefetcher();
67 void SelfDestruct();
68
69 // AutocompleteControllerDelegate:
70 virtual void OnResultChanged(bool default_match_changed) OVERRIDE;
71
72 scoped_ptr<AutocompleteController> controller_;
73 base::OneShotTimer<ZeroSuggestPrefetcher> expire_timer_;
74 };
75
76 ZeroSuggestPrefetcher::ZeroSuggestPrefetcher(Profile* profile) : controller_(
77 new AutocompleteController(profile, this,
78 AutocompleteProvider::TYPE_ZERO_SUGGEST)) {
79 // Creating an arbitrary fake_request_source to avoid passing in an invalid
80 // AutocompleteInput object.
81 base::string16 fake_request_source(base::ASCIIToUTF16(
82 "http://www.foobarbazblah.com"));
83 controller_->StartZeroSuggest(AutocompleteInput(
84 fake_request_source,
85 base::string16::npos,
86 base::string16(),
87 GURL(fake_request_source),
88 AutocompleteInput::INVALID_SPEC,
89 false,
90 false,
91 true,
92 true));
93 // Delete ourselves after 10s. This is enough time to cache results or
94 // give up if the results haven't been received.
95 expire_timer_.Start(FROM_HERE,
96 base::TimeDelta::FromMilliseconds(10000),
97 this, &ZeroSuggestPrefetcher::SelfDestruct);
98 }
99
100 ZeroSuggestPrefetcher::~ZeroSuggestPrefetcher() {
101 }
102
103 void ZeroSuggestPrefetcher::SelfDestruct() {
104 delete this;
105 }
106
107 void ZeroSuggestPrefetcher::OnResultChanged(bool default_match_changed) {
108 // Nothing to do here, the results have been cached.
109 // We don't want to trigger deletion here because this is being called by the
110 // AutocompleteController object.
111 }
112
113 } // namespace
114
115 AutocompleteControllerAndroid::AutocompleteControllerAndroid(Profile* profile)
116 : autocomplete_controller_(new AutocompleteController(
117 profile, this, kAndroidAutocompleteProviders)),
118 inside_classify_(false),
119 profile_(profile) {
120 }
121
122 void AutocompleteControllerAndroid::Start(JNIEnv* env,
123 jobject obj,
124 jstring j_text,
125 jstring j_desired_tld,
126 jstring j_current_url,
127 bool prevent_inline_autocomplete,
128 bool prefer_keyword,
129 bool allow_exact_keyword_match,
130 bool want_asynchronous_matches) {
131 if (!autocomplete_controller_)
132 return;
133
134 base::string16 desired_tld;
135 GURL current_url;
136 if (j_current_url != NULL)
137 current_url = GURL(ConvertJavaStringToUTF16(env, j_current_url));
138 if (j_desired_tld != NULL)
139 desired_tld = ConvertJavaStringToUTF16(env, j_desired_tld);
140 base::string16 text = ConvertJavaStringToUTF16(env, j_text);
141 AutocompleteInput::PageClassification page_classification =
142 AutocompleteInput::OTHER;
143 input_ = AutocompleteInput(text,
144 base::string16::npos,
145 desired_tld,
146 current_url,
147 page_classification,
148 prevent_inline_autocomplete,
149 prefer_keyword,
150 allow_exact_keyword_match,
151 want_asynchronous_matches);
152 autocomplete_controller_->Start(input_);
153 }
154
155 ScopedJavaLocalRef<jobject> AutocompleteControllerAndroid::Classify(
156 JNIEnv* env,
157 jobject obj,
158 jstring j_text) {
159 if (!autocomplete_controller_)
160 return ScopedJavaLocalRef<jobject>();
161
162 inside_classify_ = true;
163 Start(env, obj, j_text, NULL, NULL, true, false, false, false);
164 inside_classify_ = false;
165 DCHECK(autocomplete_controller_->done());
166 const AutocompleteResult& result = autocomplete_controller_->result();
167 if (result.empty())
168 return ScopedJavaLocalRef<jobject>();
169
170 return BuildOmniboxSuggestion(env, *result.begin());
171 }
172
173 void AutocompleteControllerAndroid::StartZeroSuggest(
174 JNIEnv* env,
175 jobject obj,
176 jstring j_omnibox_text,
177 jstring j_current_url,
178 jboolean is_query_in_omnibox,
179 jboolean focused_from_fakebox) {
180 if (!autocomplete_controller_)
181 return;
182
183 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
184 const GURL current_url = GURL(url);
185 base::string16 omnibox_text = ConvertJavaStringToUTF16(env, j_omnibox_text);
186
187 // If omnibox text is empty, set it to the current URL for the purposes of
188 // populating the verbatim match.
189 if (omnibox_text.empty())
190 omnibox_text = url;
191
192 input_ = AutocompleteInput(
193 omnibox_text, base::string16::npos, base::string16(), current_url,
194 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox),
195 false, false, true, true);
196 autocomplete_controller_->StartZeroSuggest(input_);
197 }
198
199 void AutocompleteControllerAndroid::Stop(JNIEnv* env,
200 jobject obj,
201 bool clear_results) {
202 if (autocomplete_controller_ != NULL)
203 autocomplete_controller_->Stop(clear_results);
204 }
205
206 void AutocompleteControllerAndroid::ResetSession(JNIEnv* env, jobject obj) {
207 if (autocomplete_controller_ != NULL)
208 autocomplete_controller_->ResetSession();
209 }
210
211 void AutocompleteControllerAndroid::OnSuggestionSelected(
212 JNIEnv* env,
213 jobject obj,
214 jint selected_index,
215 jstring j_current_url,
216 jboolean is_query_in_omnibox,
217 jboolean focused_from_fakebox,
218 jlong elapsed_time_since_first_modified,
219 jobject j_web_contents) {
220 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
221 const GURL current_url = GURL(url);
222 AutocompleteInput::PageClassification current_page_classification =
223 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox);
224 const base::TimeTicks& now(base::TimeTicks::Now());
225 content::WebContents* web_contents =
226 content::WebContents::FromJavaWebContents(j_web_contents);
227
228 OmniboxLog log(
229 input_.text(),
230 false, /* don't know */
231 input_.type(),
232 true,
233 selected_index,
234 false,
235 SessionID::IdForTab(web_contents),
236 current_page_classification,
237 base::TimeDelta::FromMilliseconds(elapsed_time_since_first_modified),
238 base::string16::npos,
239 now - autocomplete_controller_->last_time_default_match_changed(),
240 autocomplete_controller_->result());
241 autocomplete_controller_->AddProvidersInfo(&log.providers_info);
242
243 content::NotificationService::current()->Notify(
244 chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
245 content::Source<Profile>(profile_),
246 content::Details<OmniboxLog>(&log));
247 }
248
249 void AutocompleteControllerAndroid::DeleteSuggestion(JNIEnv* env,
250 jobject obj,
251 int selected_index) {
252 const AutocompleteResult& result = autocomplete_controller_->result();
253 const AutocompleteMatch& match = result.match_at(selected_index);
254 if (match.SupportsDeletion())
255 autocomplete_controller_->DeleteMatch(match);
256 }
257
258 ScopedJavaLocalRef<jstring>
259 AutocompleteControllerAndroid::UpdateMatchDestinationURL(
260 JNIEnv* env,
261 jobject obj,
262 jint selected_index,
263 jlong elapsed_time_since_input_change) {
264 // In rare cases, we navigate to cached matches and the underlying result
265 // has already been cleared, in that case ignore the URL update.
266 if (autocomplete_controller_->result().empty())
267 return ScopedJavaLocalRef<jstring>();
268
269 AutocompleteMatch match(
270 autocomplete_controller_->result().match_at(selected_index));
271 autocomplete_controller_->UpdateMatchDestinationURL(
272 base::TimeDelta::FromMilliseconds(elapsed_time_since_input_change),
273 &match);
274 return ConvertUTF8ToJavaString(env, match.destination_url.spec());
275 }
276
277 void AutocompleteControllerAndroid::Shutdown() {
278 autocomplete_controller_.reset();
279
280 JNIEnv* env = AttachCurrentThread();
281 ScopedJavaLocalRef<jobject> java_bridge =
282 weak_java_autocomplete_controller_android_.get(env);
283 if (java_bridge.obj())
284 Java_AutocompleteController_notifyNativeDestroyed(env, java_bridge.obj());
285
286 weak_java_autocomplete_controller_android_.reset();
287 }
288
289 // static
290 AutocompleteControllerAndroid*
291 AutocompleteControllerAndroid::Factory::GetForProfile(
292 Profile* profile, JNIEnv* env, jobject obj) {
293 AutocompleteControllerAndroid* bridge =
294 static_cast<AutocompleteControllerAndroid*>(
295 GetInstance()->GetServiceForBrowserContext(profile, true));
296 bridge->InitJNI(env, obj);
297 return bridge;
298 }
299
300 AutocompleteControllerAndroid::Factory*
301 AutocompleteControllerAndroid::Factory::GetInstance() {
302 return Singleton<AutocompleteControllerAndroid::Factory>::get();
303 }
304
305 content::BrowserContext*
306 AutocompleteControllerAndroid::Factory::GetBrowserContextToUse(
307 content::BrowserContext* context) const {
308 return chrome::GetBrowserContextOwnInstanceInIncognito(context);
309 }
310
311 AutocompleteControllerAndroid::Factory::Factory()
312 : BrowserContextKeyedServiceFactory(
313 "AutocompleteControllerAndroid",
314 BrowserContextDependencyManager::GetInstance()) {
315 DependsOn(ShortcutsBackendFactory::GetInstance());
316 }
317
318 AutocompleteControllerAndroid::Factory::~Factory() {
319 }
320
321 KeyedService* AutocompleteControllerAndroid::Factory::BuildServiceInstanceFor(
322 content::BrowserContext* profile) const {
323 return new AutocompleteControllerAndroid(static_cast<Profile*>(profile));
324 }
325
326 AutocompleteControllerAndroid::~AutocompleteControllerAndroid() {
327 }
328
329 void AutocompleteControllerAndroid::InitJNI(JNIEnv* env, jobject obj) {
330 weak_java_autocomplete_controller_android_ =
331 JavaObjectWeakGlobalRef(env, obj);
332 }
333
334 void AutocompleteControllerAndroid::OnResultChanged(
335 bool default_match_changed) {
336 if (autocomplete_controller_.get() != NULL && !inside_classify_)
337 NotifySuggestionsReceived(autocomplete_controller_->result());
338 }
339
340 void AutocompleteControllerAndroid::NotifySuggestionsReceived(
341 const AutocompleteResult& autocomplete_result) {
342 JNIEnv* env = AttachCurrentThread();
343 ScopedJavaLocalRef<jobject> java_bridge =
344 weak_java_autocomplete_controller_android_.get(env);
345 if (!java_bridge.obj())
346 return;
347
348 ScopedJavaLocalRef<jobject> suggestion_list_obj =
349 Java_AutocompleteController_createOmniboxSuggestionList(
350 env, autocomplete_result.size());
351 for (size_t i = 0; i < autocomplete_result.size(); ++i) {
352 ScopedJavaLocalRef<jobject> j_omnibox_suggestion =
353 BuildOmniboxSuggestion(env, autocomplete_result.match_at(i));
354 Java_AutocompleteController_addOmniboxSuggestionToList(
355 env, suggestion_list_obj.obj(), j_omnibox_suggestion.obj());
356 }
357
358 // Get the inline-autocomplete text.
359 const AutocompleteResult::const_iterator default_match(
360 autocomplete_result.default_match());
361 base::string16 inline_autocomplete_text;
362 if (default_match != autocomplete_result.end()) {
363 inline_autocomplete_text = default_match->inline_autocompletion;
364 }
365 ScopedJavaLocalRef<jstring> inline_text =
366 ConvertUTF16ToJavaString(env, inline_autocomplete_text);
367 jlong j_autocomplete_result =
368 reinterpret_cast<intptr_t>(&(autocomplete_result));
369 Java_AutocompleteController_onSuggestionsReceived(env,
370 java_bridge.obj(),
371 suggestion_list_obj.obj(),
372 inline_text.obj(),
373 j_autocomplete_result);
374 }
375
376 ScopedJavaLocalRef<jobject>
377 AutocompleteControllerAndroid::BuildOmniboxSuggestion(
378 JNIEnv* env,
379 const AutocompleteMatch& match) {
380 ScopedJavaLocalRef<jstring> contents =
381 ConvertUTF16ToJavaString(env, match.contents);
382 ScopedJavaLocalRef<jstring> description =
383 ConvertUTF16ToJavaString(env, match.description);
384 ScopedJavaLocalRef<jstring> fill_into_edit =
385 ConvertUTF16ToJavaString(env, match.fill_into_edit);
386 ScopedJavaLocalRef<jstring> destination_url =
387 ConvertUTF8ToJavaString(env, match.destination_url.spec());
388 // Note that we are also removing 'www' host from formatted url.
389 ScopedJavaLocalRef<jstring> formatted_url = ConvertUTF16ToJavaString(env,
390 FormatURLUsingAcceptLanguages(match.stripped_destination_url));
391 return Java_AutocompleteController_buildOmniboxSuggestion(
392 env,
393 match.type,
394 match.relevance,
395 match.transition,
396 contents.obj(),
397 description.obj(),
398 fill_into_edit.obj(),
399 destination_url.obj(),
400 formatted_url.obj(),
401 match.starred,
402 match.SupportsDeletion());
403 }
404
405 base::string16 AutocompleteControllerAndroid::FormatURLUsingAcceptLanguages(
406 GURL url) {
407 if (profile_ == NULL)
408 return base::string16();
409
410 std::string languages(
411 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
412
413 return net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
414 net::UnescapeRule::SPACES, NULL, NULL, NULL);
415 }
416
417 AutocompleteInput::PageClassification
418 AutocompleteControllerAndroid::ClassifyPage(const GURL& gurl,
419 bool is_query_in_omnibox,
420 bool focused_from_fakebox) const {
421 if (!gurl.is_valid())
422 return AutocompleteInput::INVALID_SPEC;
423
424 const std::string& url = gurl.spec();
425
426 if (gurl.SchemeIs(content::kChromeUIScheme) &&
427 gurl.host() == chrome::kChromeUINewTabHost) {
428 return AutocompleteInput::NTP;
429 }
430 if (url == chrome::kChromeUINativeNewTabURL) {
431 return focused_from_fakebox ?
432 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS :
433 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS;
434 }
435 if (url == content::kAboutBlankURL)
436 return AutocompleteInput::BLANK;
437
438 if (url == profile_->GetPrefs()->GetString(prefs::kHomePage))
439 return AutocompleteInput::HOME_PAGE;
440 if (is_query_in_omnibox)
441 return AutocompleteInput::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT;
442
443 bool is_search_url = TemplateURLServiceFactory::GetForProfile(profile_)->
444 IsSearchResultsPageFromDefaultSearchProvider(gurl);
445 if (is_search_url)
446 return AutocompleteInput::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT;
447 return AutocompleteInput::OTHER;
448 }
449
450 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
451 Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
452 if (!profile)
453 return 0;
454
455 AutocompleteControllerAndroid* native_bridge =
456 AutocompleteControllerAndroid::Factory::GetForProfile(profile, env, obj);
457 return reinterpret_cast<intptr_t>(native_bridge);
458 }
459
460 static jstring QualifyPartialURLQuery(
461 JNIEnv* env, jclass clazz, jstring jquery) {
462 Profile* profile = ProfileManager::GetActiveUserProfile();
463 if (!profile)
464 return NULL;
465 AutocompleteMatch match;
466 base::string16 query_string(ConvertJavaStringToUTF16(env, jquery));
467 AutocompleteClassifierFactory::GetForProfile(profile)->Classify(
468 query_string,
469 false,
470 false,
471 AutocompleteInput::INVALID_SPEC,
472 &match,
473 NULL);
474 if (!match.destination_url.is_valid())
475 return NULL;
476
477 // Only return a URL if the match is a URL type.
478 if (match.type != AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
479 match.type != AutocompleteMatchType::HISTORY_URL &&
480 match.type != AutocompleteMatchType::NAVSUGGEST)
481 return NULL;
482
483 // As we are returning to Java, it is fine to call Release().
484 return ConvertUTF8ToJavaString(env, match.destination_url.spec()).Release();
485 }
486
487 static void PrefetchZeroSuggestResults(JNIEnv* env, jclass clazz) {
488 Profile* profile = ProfileManager::GetActiveUserProfile();
489 if (!profile)
490 return;
491
492 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial())
493 return;
494
495 // ZeroSuggestPrefetcher deletes itself after it's done prefetching.
496 new ZeroSuggestPrefetcher(profile);
497 }
498
499 // Register native methods
500 bool RegisterAutocompleteControllerAndroid(JNIEnv* env) {
501 return RegisterNativesImpl(env);
502 }
OLDNEW
« no previous file with comments | « chrome/browser/android/omnibox/autocomplete_controller_android.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698