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

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: Fix a bug in OmniboxSuggestion.hashCode and rename weak reference in autocomplete controller 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 2011 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 // TODO(newt): just use chrome::kChromeUINativeNewTabURL once it's landed.
54 const char kChromeUINativeNewTabURL[] = "chrome-native://newtab/";
Ted C 2014/05/14 00:49:23 this has in fact landed, so mind cleaning up the T
Maria 2014/05/14 01:54:22 Done.
55
56 const int kAndroidAutocompleteProviders =
57 AutocompleteClassifier::kDefaultOmniboxProviders;
58
59 /**
60 * A prefetcher class responsible for triggering zero suggest prefetch.
61 * The prefetch occurs as a side-effect of calling StartZeroSuggest() on
62 * the AutocompleteController object.
63 */
64 class ZeroSuggestPrefetcher : public AutocompleteControllerDelegate {
65 public:
66 explicit ZeroSuggestPrefetcher(Profile* profile);
67
68 private:
69 virtual ~ZeroSuggestPrefetcher();
70 void SelfDestruct();
71
72 // AutocompleteControllerDelegate:
73 virtual void OnResultChanged(bool default_match_changed) OVERRIDE;
74
75 scoped_ptr<AutocompleteController> controller_;
76 base::OneShotTimer<ZeroSuggestPrefetcher> expire_timer_;
77 };
78
79 ZeroSuggestPrefetcher::ZeroSuggestPrefetcher(Profile* profile) : controller_(
80 new AutocompleteController(profile, this,
81 AutocompleteProvider::TYPE_ZERO_SUGGEST)) {
82 // Creating an arbitrary fake_request_source to avoid passing in an invalid
83 // AutocompleteInput object.
84 base::string16 fake_request_source(base::ASCIIToUTF16(
85 "http://www.foobarbazblah.com"));
86 controller_->StartZeroSuggest(AutocompleteInput(
87 fake_request_source, base::string16::npos, base::string16(),
88 GURL(fake_request_source), AutocompleteInput::INVALID_SPEC,
89 false, false, true, true));
90 // Delete ourselves after 10s. This is enough time to cache results or
91 // give up if the results haven't been received.
92 expire_timer_.Start(FROM_HERE,
93 base::TimeDelta::FromMilliseconds(10000),
94 this, &ZeroSuggestPrefetcher::SelfDestruct);
95 }
96
97 ZeroSuggestPrefetcher::~ZeroSuggestPrefetcher() {
98 }
99
100 void ZeroSuggestPrefetcher::SelfDestruct() {
101 delete this;
102 }
103
104 void ZeroSuggestPrefetcher::OnResultChanged(bool default_match_changed) {
105 // Nothing to do here, the results have been cached.
106 // We don't want to trigger deletion here because this is being called by the
107 // AutocompleteController object.
108 }
109
110 } // namespace
111
112 AutocompleteControllerAndroid::AutocompleteControllerAndroid(Profile* profile)
113 : autocomplete_controller_(new AutocompleteController(
114 profile, this, kAndroidAutocompleteProviders)),
115 inside_classify_(false),
116 profile_(profile) {
117 }
118
119 void AutocompleteControllerAndroid::Start(JNIEnv* env,
120 jobject obj,
121 jstring j_text,
122 jstring j_desired_tld,
123 jstring j_current_url,
124 bool prevent_inline_autocomplete,
125 bool prefer_keyword,
126 bool allow_exact_keyword_match,
127 bool want_asynchronous_matches) {
128 if (autocomplete_controller_ == NULL)
Ted C 2014/05/14 00:49:23 I think in chrome, it is now more common to see i
Maria 2014/05/14 01:54:22 Done.
129 return;
130
131 base::string16 desired_tld;
132 GURL current_url;
133 if (j_current_url != NULL)
134 current_url = GURL(ConvertJavaStringToUTF16(env, j_current_url));
135 if (j_desired_tld != NULL)
136 desired_tld = ConvertJavaStringToUTF16(env, j_desired_tld);
137 base::string16 text = ConvertJavaStringToUTF16(env, j_text);
138 AutocompleteInput::PageClassification page_classification =
139 AutocompleteInput::OTHER;
140 input_ = AutocompleteInput(
141 text, base::string16::npos, desired_tld, current_url,
142 page_classification, prevent_inline_autocomplete, prefer_keyword,
143 allow_exact_keyword_match, want_asynchronous_matches);
144 autocomplete_controller_->Start(input_);
145 }
146
147 ScopedJavaLocalRef<jobject> AutocompleteControllerAndroid::Classify(JNIEnv* env,
148 jobject obj,
Ted C 2014/05/14 00:49:23 indenting is off on these params (and else where b
Maria 2014/05/14 01:54:22 Done.
149 jstring j_text) {
150 if (autocomplete_controller_ == NULL)
151 return ScopedJavaLocalRef<jobject>();
152
153 inside_classify_ = true;
154 Start(env, obj, j_text, NULL, NULL, true, false, false, false);
155 inside_classify_ = false;
156 DCHECK(autocomplete_controller_->done());
157 const AutocompleteResult& result = autocomplete_controller_->result();
158 if (result.empty())
159 return ScopedJavaLocalRef<jobject>();
160
161 return BuildOmniboxSuggestion(env, *result.begin());
162 }
163
164 void AutocompleteControllerAndroid::StartZeroSuggest(JNIEnv* env,
165 jobject obj,
166 jstring j_omnibox_text,
167 jstring j_current_url,
168 jboolean is_query_in_omnibox,
169 jboolean focused_from_fakebox) {
170 if (autocomplete_controller_ == NULL)
171 return;
172
173 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
174 const GURL current_url = GURL(url);
175 base::string16 omnibox_text = ConvertJavaStringToUTF16(env, j_omnibox_text);
176
177 // If omnibox text is empty, set it to the current URL for the purposes of
178 // populating the verbatim match.
179 if (omnibox_text.empty())
180 omnibox_text = url;
181
182 input_ = AutocompleteInput(
183 omnibox_text, base::string16::npos, base::string16(), current_url,
184 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox),
185 false, false, true, true);
186 autocomplete_controller_->StartZeroSuggest(input_);
187 }
188
189 void AutocompleteControllerAndroid::Stop(JNIEnv* env,
190 jobject obj,
191 bool clear_results) {
192 if (autocomplete_controller_ != NULL)
193 autocomplete_controller_->Stop(clear_results);
194 }
195
196 void AutocompleteControllerAndroid::ResetSession(JNIEnv* env, jobject obj) {
197 if (autocomplete_controller_ != NULL)
198 autocomplete_controller_->ResetSession();
199 }
200
201 void AutocompleteControllerAndroid::OnSuggestionSelected(
202 JNIEnv* env,
203 jobject obj,
204 jint selected_index,
205 jstring j_current_url,
206 jboolean is_query_in_omnibox,
207 jboolean focused_from_fakebox,
208 jlong elapsed_time_since_first_modified,
209 jobject j_web_contents) {
210 base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
211 const GURL current_url = GURL(url);
212 AutocompleteInput::PageClassification current_page_classification =
213 ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox);
214 const base::TimeTicks& now(base::TimeTicks::Now());
215 content::WebContents* web_contents =
216 content::WebContents::FromJavaWebContents(j_web_contents);
217
218 OmniboxLog log(
219 input_.text(),
220 false, /* don't know */
221 input_.type(),
222 true,
223 selected_index,
224 false,
225 SessionID::IdForTab(web_contents),
226 current_page_classification,
227 base::TimeDelta::FromMilliseconds(elapsed_time_since_first_modified),
228 base::string16::npos,
229 now - autocomplete_controller_->last_time_default_match_changed(),
230 autocomplete_controller_->result());
231 autocomplete_controller_->AddProvidersInfo(&log.providers_info);
232
233 content::NotificationService::current()->Notify(
234 chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
235 content::Source<Profile>(profile_),
236 content::Details<OmniboxLog>(&log));
237 }
238
239 void AutocompleteControllerAndroid::DeleteSuggestion(JNIEnv* env,
240 jobject obj,
241 int selected_index) {
242 const AutocompleteResult& result = autocomplete_controller_->result();
243 const AutocompleteMatch& match = result.match_at(selected_index);
244 if (match.SupportsDeletion())
245 autocomplete_controller_->DeleteMatch(match);
246 }
247
248 ScopedJavaLocalRef<jstring>
249 AutocompleteControllerAndroid::UpdateMatchDestinationURL(
250 JNIEnv* env,
251 jobject obj,
252 jint selected_index,
253 jlong elapsed_time_since_input_change) {
254 // In rare cases, we navigate to cached matches and the underlying result
255 // has already been cleared, in that case ignore the URL update.
256 if (autocomplete_controller_->result().empty())
257 return ScopedJavaLocalRef<jstring>();
258
259 AutocompleteMatch match(
260 autocomplete_controller_->result().match_at(selected_index));
261 autocomplete_controller_->UpdateMatchDestinationURL(
262 base::TimeDelta::FromMilliseconds(elapsed_time_since_input_change),
263 &match);
264 return ConvertUTF8ToJavaString(env, match.destination_url.spec());
265 }
266
267 void AutocompleteControllerAndroid::Shutdown() {
268 autocomplete_controller_.reset();
269
270 JNIEnv* env = AttachCurrentThread();
271 ScopedJavaLocalRef<jobject> java_bridge =
272 weak_java_autocomplete_controller_android_.get(env);
273 if (java_bridge.obj())
274 Java_AutocompleteController_notifyNativeDestroyed(env, java_bridge.obj());
275
276 weak_java_autocomplete_controller_android_.reset();
277 }
278
279 // static
280 AutocompleteControllerAndroid*
281 AutocompleteControllerAndroid::Factory::GetForProfile(
282 Profile* profile, JNIEnv* env, jobject obj) {
283 AutocompleteControllerAndroid* bridge =
284 static_cast<AutocompleteControllerAndroid*>(
285 GetInstance()->GetServiceForBrowserContext(profile, true));
286 bridge->InitJNI(env, obj);
287 return bridge;
288 }
289
290 AutocompleteControllerAndroid::Factory*
291 AutocompleteControllerAndroid::Factory::GetInstance() {
292 return Singleton<AutocompleteControllerAndroid::Factory>::get();
293 }
294
295 content::BrowserContext*
296 AutocompleteControllerAndroid::Factory::GetBrowserContextToUse(
297 content::BrowserContext* context) const {
298 return chrome::GetBrowserContextOwnInstanceInIncognito(context);
299 }
300
301 AutocompleteControllerAndroid::Factory::Factory()
302 : BrowserContextKeyedServiceFactory(
303 "AutocompleteControllerAndroid",
Maria 2014/05/14 01:54:22 Btw, I changed the key string here and I assume it
Ted C 2014/05/15 18:10:52 Correct, you just want the name to be unique...and
304 BrowserContextDependencyManager::GetInstance()) {
305 DependsOn(ShortcutsBackendFactory::GetInstance());
306 }
307
308 AutocompleteControllerAndroid::Factory::~Factory() {
309 }
310
311 KeyedService* AutocompleteControllerAndroid::Factory::BuildServiceInstanceFor(
312 content::BrowserContext* profile) const {
313 return new AutocompleteControllerAndroid(static_cast<Profile*>(profile));
314 }
315
316 AutocompleteControllerAndroid::~AutocompleteControllerAndroid() {
317 }
318
319 void AutocompleteControllerAndroid::InitJNI(JNIEnv* env, jobject obj) {
320 weak_java_autocomplete_controller_android_ =
321 JavaObjectWeakGlobalRef(env, obj);
322 }
323
324 void AutocompleteControllerAndroid::OnResultChanged(
325 bool default_match_changed) {
326 if (autocomplete_controller_.get() != NULL && !inside_classify_)
327 NotifySuggestionsReceived(autocomplete_controller_->result());
328 }
329
330 void AutocompleteControllerAndroid::NotifySuggestionsReceived(
331 const AutocompleteResult& autocomplete_result) {
332 JNIEnv* env = AttachCurrentThread();
333 ScopedJavaLocalRef<jobject> java_bridge =
334 weak_java_autocomplete_controller_android_.get(env);
335 if (!java_bridge.obj())
336 return;
337
338 ScopedJavaLocalRef<jobject> suggestion_list_obj =
339 Java_AutocompleteController_createOmniboxSuggestionList(
340 env, autocomplete_result.size());
341 for (size_t i = 0; i < autocomplete_result.size(); ++i) {
342 ScopedJavaLocalRef<jobject> j_omnibox_suggestion =
343 BuildOmniboxSuggestion(env, autocomplete_result.match_at(i));
344 Java_AutocompleteController_addOmniboxSuggestionToList(
345 env, suggestion_list_obj.obj(), j_omnibox_suggestion.obj());
346 }
347
348 // Get the inline-autocomplete text.
349 const AutocompleteResult::const_iterator default_match(
350 autocomplete_result.default_match());
351 base::string16 inline_autocomplete_text;
352 if (default_match != autocomplete_result.end()) {
353 inline_autocomplete_text = default_match->inline_autocompletion;
354 }
355 ScopedJavaLocalRef<jstring> inline_text =
356 ConvertUTF16ToJavaString(env, inline_autocomplete_text);
357 jlong j_autocomplete_result =
358 reinterpret_cast<intptr_t>(&(autocomplete_result));
359 Java_AutocompleteController_onSuggestionsReceived(env,
360 java_bridge.obj(),
361 suggestion_list_obj.obj(),
362 inline_text.obj(),
363 j_autocomplete_result);
364 }
365
366 ScopedJavaLocalRef<jobject>
367 AutocompleteControllerAndroid::BuildOmniboxSuggestion(
368 JNIEnv* env,
369 const AutocompleteMatch& match) {
370 ScopedJavaLocalRef<jstring> contents =
371 ConvertUTF16ToJavaString(env, match.contents);
372 ScopedJavaLocalRef<jstring> description =
373 ConvertUTF16ToJavaString(env, match.description);
374 ScopedJavaLocalRef<jstring> fill_into_edit =
375 ConvertUTF16ToJavaString(env, match.fill_into_edit);
376 ScopedJavaLocalRef<jstring> destination_url =
377 ConvertUTF8ToJavaString(env, match.destination_url.spec());
378 // Note that we are also removing 'www' host from formatted url.
379 ScopedJavaLocalRef<jstring> formatted_url = ConvertUTF16ToJavaString(env,
380 FormatURLUsingAcceptLanguages(match.stripped_destination_url));
381 return Java_AutocompleteController_buildOmniboxSuggestion(
382 env, match.type, match.relevance,
Ted C 2014/05/14 00:49:23 I think each one of these needs to go on a new lin
Maria 2014/05/14 01:54:22 Done. For the record, I think it's crazy to be pas
383 match.transition, contents.obj(), description.obj(),
384 fill_into_edit.obj(), destination_url.obj(), formatted_url.obj(),
385 match.starred, match.SupportsDeletion());
386 }
387
388 base::string16 AutocompleteControllerAndroid::FormatURLUsingAcceptLanguages(
389 GURL url) {
390 if (profile_ == NULL) {
Ted C 2014/05/14 00:49:23 no braces needed.
Maria 2014/05/14 01:54:22 Done.
391 return base::string16();
392 }
393 std::string languages(
394 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
395
396 return net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
397 net::UnescapeRule::SPACES, NULL, NULL, NULL);
398 }
399
400 AutocompleteInput::PageClassification
401 AutocompleteControllerAndroid::ClassifyPage(const GURL& gurl,
402 bool is_query_in_omnibox,
403 bool focused_from_fakebox) const {
404 if (!gurl.is_valid())
405 return AutocompleteInput::INVALID_SPEC;
406
407 const std::string& url = gurl.spec();
408
409 if (gurl.SchemeIs(content::kChromeUIScheme) &&
410 gurl.host() == chrome::kChromeUINewTabHost) {
411 return AutocompleteInput::NTP;
412 }
413 if (url == kChromeUINativeNewTabURL) {
414 return focused_from_fakebox ?
415 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS :
416 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS;
417 }
418 if (url == content::kAboutBlankURL)
419 return AutocompleteInput::BLANK;
420
421 if (url == profile_->GetPrefs()->GetString(prefs::kHomePage))
422 return AutocompleteInput::HOME_PAGE;
423 if (is_query_in_omnibox)
424 return AutocompleteInput::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT;
425
426 bool is_search_url = TemplateURLServiceFactory::GetForProfile(profile_)->
427 IsSearchResultsPageFromDefaultSearchProvider(gurl);
428 if (is_search_url)
429 return AutocompleteInput::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT;
430 return AutocompleteInput::OTHER;
431 }
432
433 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
434 Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
435 if (!profile)
436 return 0;
437
438 AutocompleteControllerAndroid* native_bridge =
439 AutocompleteControllerAndroid::Factory::GetForProfile(profile, env, obj);
440 return reinterpret_cast<intptr_t>(native_bridge);
441 }
442
443 static jstring QualifyPartialURLQuery(
444 JNIEnv* env, jclass clazz, jstring jquery) {
445 Profile* profile = ProfileManager::GetActiveUserProfile();
446 if (!profile)
447 return NULL;
448 AutocompleteMatch match;
449 base::string16 query_string(ConvertJavaStringToUTF16(env, jquery));
450 AutocompleteClassifierFactory::GetForProfile(profile)->Classify(
451 query_string, false, false, AutocompleteInput::INVALID_SPEC, &match,
452 NULL);
453 if (!match.destination_url.is_valid())
454 return NULL;
455
456 // Only return a URL if the match is a URL type.
457 if (match.type != AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
458 match.type != AutocompleteMatchType::HISTORY_URL &&
459 match.type != AutocompleteMatchType::NAVSUGGEST)
460 return NULL;
461
462 // As we are returning to Java, it is fine to call Release().
463 return ConvertUTF8ToJavaString(env, match.destination_url.spec()).Release();
464 }
465
466 static void PrefetchZeroSuggestResults(JNIEnv* env, jclass clazz) {
467 Profile* profile = ProfileManager::GetActiveUserProfile();
468 if (!profile)
469 return;
470
471 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial())
472 return;
473
474 // ZeroSuggestPrefetcher deletes itself after it's done prefetching.
475 new ZeroSuggestPrefetcher(profile);
476 }
477
478 // Register native methods
479 bool RegisterAutocompleteControllerAndroid(JNIEnv* env) {
480 return RegisterNativesImpl(env);
481 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698