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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/android/omnibox/autocomplete_controller_android.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/android/omnibox/autocomplete_controller_android.cc
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
new file mode 100644
index 0000000000000000000000000000000000000000..969341e49368f7b720d25ccb85263879b12c4763
--- /dev/null
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -0,0 +1,502 @@
+// Copyright 2014 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/android/omnibox/autocomplete_controller_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/autocomplete/autocomplete_classifier.h"
+#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
+#include "chrome/browser/autocomplete/autocomplete_controller.h"
+#include "chrome/browser/autocomplete/autocomplete_input.h"
+#include "chrome/browser/autocomplete/autocomplete_match.h"
+#include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/omnibox/omnibox_field_trial.h"
+#include "chrome/browser/omnibox/omnibox_log.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/search_engines/template_url_service.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/ui/toolbar/toolbar_model.h"
+#include "chrome/common/autocomplete_match_type.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
+#include "jni/AutocompleteController_jni.h"
+#include "net/base/escape.h"
+#include "net/base/net_util.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF16;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertUTF16ToJavaString;
+
+namespace {
+
+const int kAndroidAutocompleteProviders =
+ AutocompleteClassifier::kDefaultOmniboxProviders;
+
+/**
+ * A prefetcher class responsible for triggering zero suggest prefetch.
+ * The prefetch occurs as a side-effect of calling StartZeroSuggest() on
+ * the AutocompleteController object.
+ */
+class ZeroSuggestPrefetcher : public AutocompleteControllerDelegate {
+ public:
+ explicit ZeroSuggestPrefetcher(Profile* profile);
+
+ private:
+ virtual ~ZeroSuggestPrefetcher();
+ void SelfDestruct();
+
+ // AutocompleteControllerDelegate:
+ virtual void OnResultChanged(bool default_match_changed) OVERRIDE;
+
+ scoped_ptr<AutocompleteController> controller_;
+ base::OneShotTimer<ZeroSuggestPrefetcher> expire_timer_;
+};
+
+ZeroSuggestPrefetcher::ZeroSuggestPrefetcher(Profile* profile) : controller_(
+ new AutocompleteController(profile, this,
+ AutocompleteProvider::TYPE_ZERO_SUGGEST)) {
+ // Creating an arbitrary fake_request_source to avoid passing in an invalid
+ // AutocompleteInput object.
+ base::string16 fake_request_source(base::ASCIIToUTF16(
+ "http://www.foobarbazblah.com"));
+ controller_->StartZeroSuggest(AutocompleteInput(
+ fake_request_source,
+ base::string16::npos,
+ base::string16(),
+ GURL(fake_request_source),
+ AutocompleteInput::INVALID_SPEC,
+ false,
+ false,
+ true,
+ true));
+ // Delete ourselves after 10s. This is enough time to cache results or
+ // give up if the results haven't been received.
+ expire_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(10000),
+ this, &ZeroSuggestPrefetcher::SelfDestruct);
+}
+
+ZeroSuggestPrefetcher::~ZeroSuggestPrefetcher() {
+}
+
+void ZeroSuggestPrefetcher::SelfDestruct() {
+ delete this;
+}
+
+void ZeroSuggestPrefetcher::OnResultChanged(bool default_match_changed) {
+ // Nothing to do here, the results have been cached.
+ // We don't want to trigger deletion here because this is being called by the
+ // AutocompleteController object.
+}
+
+} // namespace
+
+AutocompleteControllerAndroid::AutocompleteControllerAndroid(Profile* profile)
+ : autocomplete_controller_(new AutocompleteController(
+ profile, this, kAndroidAutocompleteProviders)),
+ inside_classify_(false),
+ profile_(profile) {
+}
+
+void AutocompleteControllerAndroid::Start(JNIEnv* env,
+ jobject obj,
+ jstring j_text,
+ jstring j_desired_tld,
+ jstring j_current_url,
+ bool prevent_inline_autocomplete,
+ bool prefer_keyword,
+ bool allow_exact_keyword_match,
+ bool want_asynchronous_matches) {
+ if (!autocomplete_controller_)
+ return;
+
+ base::string16 desired_tld;
+ GURL current_url;
+ if (j_current_url != NULL)
+ current_url = GURL(ConvertJavaStringToUTF16(env, j_current_url));
+ if (j_desired_tld != NULL)
+ desired_tld = ConvertJavaStringToUTF16(env, j_desired_tld);
+ base::string16 text = ConvertJavaStringToUTF16(env, j_text);
+ AutocompleteInput::PageClassification page_classification =
+ AutocompleteInput::OTHER;
+ input_ = AutocompleteInput(text,
+ base::string16::npos,
+ desired_tld,
+ current_url,
+ page_classification,
+ prevent_inline_autocomplete,
+ prefer_keyword,
+ allow_exact_keyword_match,
+ want_asynchronous_matches);
+ autocomplete_controller_->Start(input_);
+}
+
+ScopedJavaLocalRef<jobject> AutocompleteControllerAndroid::Classify(
+ JNIEnv* env,
+ jobject obj,
+ jstring j_text) {
+ if (!autocomplete_controller_)
+ return ScopedJavaLocalRef<jobject>();
+
+ inside_classify_ = true;
+ Start(env, obj, j_text, NULL, NULL, true, false, false, false);
+ inside_classify_ = false;
+ DCHECK(autocomplete_controller_->done());
+ const AutocompleteResult& result = autocomplete_controller_->result();
+ if (result.empty())
+ return ScopedJavaLocalRef<jobject>();
+
+ return BuildOmniboxSuggestion(env, *result.begin());
+}
+
+void AutocompleteControllerAndroid::StartZeroSuggest(
+ JNIEnv* env,
+ jobject obj,
+ jstring j_omnibox_text,
+ jstring j_current_url,
+ jboolean is_query_in_omnibox,
+ jboolean focused_from_fakebox) {
+ if (!autocomplete_controller_)
+ return;
+
+ base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
+ const GURL current_url = GURL(url);
+ base::string16 omnibox_text = ConvertJavaStringToUTF16(env, j_omnibox_text);
+
+ // If omnibox text is empty, set it to the current URL for the purposes of
+ // populating the verbatim match.
+ if (omnibox_text.empty())
+ omnibox_text = url;
+
+ input_ = AutocompleteInput(
+ omnibox_text, base::string16::npos, base::string16(), current_url,
+ ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox),
+ false, false, true, true);
+ autocomplete_controller_->StartZeroSuggest(input_);
+}
+
+void AutocompleteControllerAndroid::Stop(JNIEnv* env,
+ jobject obj,
+ bool clear_results) {
+ if (autocomplete_controller_ != NULL)
+ autocomplete_controller_->Stop(clear_results);
+}
+
+void AutocompleteControllerAndroid::ResetSession(JNIEnv* env, jobject obj) {
+ if (autocomplete_controller_ != NULL)
+ autocomplete_controller_->ResetSession();
+}
+
+void AutocompleteControllerAndroid::OnSuggestionSelected(
+ JNIEnv* env,
+ jobject obj,
+ jint selected_index,
+ jstring j_current_url,
+ jboolean is_query_in_omnibox,
+ jboolean focused_from_fakebox,
+ jlong elapsed_time_since_first_modified,
+ jobject j_web_contents) {
+ base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
+ const GURL current_url = GURL(url);
+ AutocompleteInput::PageClassification current_page_classification =
+ ClassifyPage(current_url, is_query_in_omnibox, focused_from_fakebox);
+ const base::TimeTicks& now(base::TimeTicks::Now());
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(j_web_contents);
+
+ OmniboxLog log(
+ input_.text(),
+ false, /* don't know */
+ input_.type(),
+ true,
+ selected_index,
+ false,
+ SessionID::IdForTab(web_contents),
+ current_page_classification,
+ base::TimeDelta::FromMilliseconds(elapsed_time_since_first_modified),
+ base::string16::npos,
+ now - autocomplete_controller_->last_time_default_match_changed(),
+ autocomplete_controller_->result());
+ autocomplete_controller_->AddProvidersInfo(&log.providers_info);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
+ content::Source<Profile>(profile_),
+ content::Details<OmniboxLog>(&log));
+}
+
+void AutocompleteControllerAndroid::DeleteSuggestion(JNIEnv* env,
+ jobject obj,
+ int selected_index) {
+ const AutocompleteResult& result = autocomplete_controller_->result();
+ const AutocompleteMatch& match = result.match_at(selected_index);
+ if (match.SupportsDeletion())
+ autocomplete_controller_->DeleteMatch(match);
+}
+
+ScopedJavaLocalRef<jstring>
+AutocompleteControllerAndroid::UpdateMatchDestinationURL(
+ JNIEnv* env,
+ jobject obj,
+ jint selected_index,
+ jlong elapsed_time_since_input_change) {
+ // In rare cases, we navigate to cached matches and the underlying result
+ // has already been cleared, in that case ignore the URL update.
+ if (autocomplete_controller_->result().empty())
+ return ScopedJavaLocalRef<jstring>();
+
+ AutocompleteMatch match(
+ autocomplete_controller_->result().match_at(selected_index));
+ autocomplete_controller_->UpdateMatchDestinationURL(
+ base::TimeDelta::FromMilliseconds(elapsed_time_since_input_change),
+ &match);
+ return ConvertUTF8ToJavaString(env, match.destination_url.spec());
+}
+
+void AutocompleteControllerAndroid::Shutdown() {
+ autocomplete_controller_.reset();
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> java_bridge =
+ weak_java_autocomplete_controller_android_.get(env);
+ if (java_bridge.obj())
+ Java_AutocompleteController_notifyNativeDestroyed(env, java_bridge.obj());
+
+ weak_java_autocomplete_controller_android_.reset();
+}
+
+// static
+AutocompleteControllerAndroid*
+AutocompleteControllerAndroid::Factory::GetForProfile(
+ Profile* profile, JNIEnv* env, jobject obj) {
+ AutocompleteControllerAndroid* bridge =
+ static_cast<AutocompleteControllerAndroid*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+ bridge->InitJNI(env, obj);
+ return bridge;
+}
+
+AutocompleteControllerAndroid::Factory*
+AutocompleteControllerAndroid::Factory::GetInstance() {
+ return Singleton<AutocompleteControllerAndroid::Factory>::get();
+}
+
+content::BrowserContext*
+AutocompleteControllerAndroid::Factory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
+
+AutocompleteControllerAndroid::Factory::Factory()
+ : BrowserContextKeyedServiceFactory(
+ "AutocompleteControllerAndroid",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(ShortcutsBackendFactory::GetInstance());
+}
+
+AutocompleteControllerAndroid::Factory::~Factory() {
+}
+
+KeyedService* AutocompleteControllerAndroid::Factory::BuildServiceInstanceFor(
+ content::BrowserContext* profile) const {
+ return new AutocompleteControllerAndroid(static_cast<Profile*>(profile));
+}
+
+AutocompleteControllerAndroid::~AutocompleteControllerAndroid() {
+}
+
+void AutocompleteControllerAndroid::InitJNI(JNIEnv* env, jobject obj) {
+ weak_java_autocomplete_controller_android_ =
+ JavaObjectWeakGlobalRef(env, obj);
+}
+
+void AutocompleteControllerAndroid::OnResultChanged(
+ bool default_match_changed) {
+ if (autocomplete_controller_.get() != NULL && !inside_classify_)
+ NotifySuggestionsReceived(autocomplete_controller_->result());
+}
+
+void AutocompleteControllerAndroid::NotifySuggestionsReceived(
+ const AutocompleteResult& autocomplete_result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> java_bridge =
+ weak_java_autocomplete_controller_android_.get(env);
+ if (!java_bridge.obj())
+ return;
+
+ ScopedJavaLocalRef<jobject> suggestion_list_obj =
+ Java_AutocompleteController_createOmniboxSuggestionList(
+ env, autocomplete_result.size());
+ for (size_t i = 0; i < autocomplete_result.size(); ++i) {
+ ScopedJavaLocalRef<jobject> j_omnibox_suggestion =
+ BuildOmniboxSuggestion(env, autocomplete_result.match_at(i));
+ Java_AutocompleteController_addOmniboxSuggestionToList(
+ env, suggestion_list_obj.obj(), j_omnibox_suggestion.obj());
+ }
+
+ // Get the inline-autocomplete text.
+ const AutocompleteResult::const_iterator default_match(
+ autocomplete_result.default_match());
+ base::string16 inline_autocomplete_text;
+ if (default_match != autocomplete_result.end()) {
+ inline_autocomplete_text = default_match->inline_autocompletion;
+ }
+ ScopedJavaLocalRef<jstring> inline_text =
+ ConvertUTF16ToJavaString(env, inline_autocomplete_text);
+ jlong j_autocomplete_result =
+ reinterpret_cast<intptr_t>(&(autocomplete_result));
+ Java_AutocompleteController_onSuggestionsReceived(env,
+ java_bridge.obj(),
+ suggestion_list_obj.obj(),
+ inline_text.obj(),
+ j_autocomplete_result);
+}
+
+ScopedJavaLocalRef<jobject>
+AutocompleteControllerAndroid::BuildOmniboxSuggestion(
+ JNIEnv* env,
+ const AutocompleteMatch& match) {
+ ScopedJavaLocalRef<jstring> contents =
+ ConvertUTF16ToJavaString(env, match.contents);
+ ScopedJavaLocalRef<jstring> description =
+ ConvertUTF16ToJavaString(env, match.description);
+ ScopedJavaLocalRef<jstring> fill_into_edit =
+ ConvertUTF16ToJavaString(env, match.fill_into_edit);
+ ScopedJavaLocalRef<jstring> destination_url =
+ ConvertUTF8ToJavaString(env, match.destination_url.spec());
+ // Note that we are also removing 'www' host from formatted url.
+ ScopedJavaLocalRef<jstring> formatted_url = ConvertUTF16ToJavaString(env,
+ FormatURLUsingAcceptLanguages(match.stripped_destination_url));
+ return Java_AutocompleteController_buildOmniboxSuggestion(
+ env,
+ match.type,
+ match.relevance,
+ match.transition,
+ contents.obj(),
+ description.obj(),
+ fill_into_edit.obj(),
+ destination_url.obj(),
+ formatted_url.obj(),
+ match.starred,
+ match.SupportsDeletion());
+}
+
+base::string16 AutocompleteControllerAndroid::FormatURLUsingAcceptLanguages(
+ GURL url) {
+ if (profile_ == NULL)
+ return base::string16();
+
+ std::string languages(
+ profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
+
+ return net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
+ net::UnescapeRule::SPACES, NULL, NULL, NULL);
+}
+
+AutocompleteInput::PageClassification
+AutocompleteControllerAndroid::ClassifyPage(const GURL& gurl,
+ bool is_query_in_omnibox,
+ bool focused_from_fakebox) const {
+ if (!gurl.is_valid())
+ return AutocompleteInput::INVALID_SPEC;
+
+ const std::string& url = gurl.spec();
+
+ if (gurl.SchemeIs(content::kChromeUIScheme) &&
+ gurl.host() == chrome::kChromeUINewTabHost) {
+ return AutocompleteInput::NTP;
+ }
+ if (url == chrome::kChromeUINativeNewTabURL) {
+ return focused_from_fakebox ?
+ AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS :
+ AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS;
+ }
+ if (url == content::kAboutBlankURL)
+ return AutocompleteInput::BLANK;
+
+ if (url == profile_->GetPrefs()->GetString(prefs::kHomePage))
+ return AutocompleteInput::HOME_PAGE;
+ if (is_query_in_omnibox)
+ return AutocompleteInput::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT;
+
+ bool is_search_url = TemplateURLServiceFactory::GetForProfile(profile_)->
+ IsSearchResultsPageFromDefaultSearchProvider(gurl);
+ if (is_search_url)
+ return AutocompleteInput::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT;
+ return AutocompleteInput::OTHER;
+}
+
+static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
+ Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+ if (!profile)
+ return 0;
+
+ AutocompleteControllerAndroid* native_bridge =
+ AutocompleteControllerAndroid::Factory::GetForProfile(profile, env, obj);
+ return reinterpret_cast<intptr_t>(native_bridge);
+}
+
+static jstring QualifyPartialURLQuery(
+ JNIEnv* env, jclass clazz, jstring jquery) {
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ if (!profile)
+ return NULL;
+ AutocompleteMatch match;
+ base::string16 query_string(ConvertJavaStringToUTF16(env, jquery));
+ AutocompleteClassifierFactory::GetForProfile(profile)->Classify(
+ query_string,
+ false,
+ false,
+ AutocompleteInput::INVALID_SPEC,
+ &match,
+ NULL);
+ if (!match.destination_url.is_valid())
+ return NULL;
+
+ // Only return a URL if the match is a URL type.
+ if (match.type != AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
+ match.type != AutocompleteMatchType::HISTORY_URL &&
+ match.type != AutocompleteMatchType::NAVSUGGEST)
+ return NULL;
+
+ // As we are returning to Java, it is fine to call Release().
+ return ConvertUTF8ToJavaString(env, match.destination_url.spec()).Release();
+}
+
+static void PrefetchZeroSuggestResults(JNIEnv* env, jclass clazz) {
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ if (!profile)
+ return;
+
+ if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial())
+ return;
+
+ // ZeroSuggestPrefetcher deletes itself after it's done prefetching.
+ new ZeroSuggestPrefetcher(profile);
+}
+
+// Register native methods
+bool RegisterAutocompleteControllerAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
« 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