| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 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/autocomplete/extension_app_provider.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <cmath> |
| 9 |
| 10 #include "base/string16.h" |
| 11 #include "base/utf_string_conversions.h" |
| 12 #include "chrome/browser/extensions/extension_service.h" |
| 13 #include "chrome/browser/history/history.h" |
| 14 #include "chrome/browser/history/url_database.h" |
| 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "content/common/notification_service.h" |
| 17 #include "ui/base/l10n/l10n_util.h" |
| 18 |
| 19 ExtensionAppProvider::ExtensionAppProvider(ACProviderListener* listener, |
| 20 Profile* profile) |
| 21 : AutocompleteProvider(listener, profile, "ExtensionApps") { |
| 22 RegisterForNotifications(); |
| 23 RefreshAppList(); |
| 24 } |
| 25 |
| 26 void ExtensionAppProvider::Start(const AutocompleteInput& input, |
| 27 bool minimal_changes) { |
| 28 matches_.clear(); |
| 29 |
| 30 if (input.type() == AutocompleteInput::INVALID) |
| 31 return; |
| 32 |
| 33 if (!input.text().empty()) { |
| 34 std::string input_utf8 = UTF16ToUTF8(input.text()); |
| 35 for (ExtensionApps::const_iterator app = extension_apps_.begin(); |
| 36 app != extension_apps_.end(); ++app) { |
| 37 // See if the input matches this extension application. |
| 38 const std::string& name = app->first; |
| 39 const std::string& url = app->second; |
| 40 std::string::const_iterator name_iter = |
| 41 std::search(name.begin(), |
| 42 name.end(), |
| 43 input_utf8.begin(), |
| 44 input_utf8.end(), |
| 45 base::CaseInsensitiveCompare<char>()); |
| 46 std::string::const_iterator url_iter = |
| 47 std::search(url.begin(), |
| 48 url.end(), |
| 49 input_utf8.begin(), |
| 50 input_utf8.end(), |
| 51 base::CaseInsensitiveCompare<char>()); |
| 52 |
| 53 bool matches_name = name_iter != name.end(); |
| 54 bool matches_url = url_iter != url.end() && |
| 55 input.type() != AutocompleteInput::FORCED_QUERY; |
| 56 if (matches_name || matches_url) { |
| 57 // We have a match, might be a partial match. |
| 58 // TODO(finnur): Figure out what type to return here, might want to have |
| 59 // the extension icon/a generic icon show up in the Omnibox. |
| 60 AutocompleteMatch match(this, 0, false, AutocompleteMatch::HISTORY_URL); |
| 61 match.fill_into_edit = UTF8ToUTF16(url); |
| 62 match.destination_url = GURL(url); |
| 63 match.inline_autocomplete_offset = string16::npos; |
| 64 match.contents = UTF8ToUTF16(name); |
| 65 HighlightMatch(input, &match.contents_class, name_iter, name); |
| 66 match.description = UTF8ToUTF16(url); |
| 67 HighlightMatch(input, &match.description_class, url_iter, url); |
| 68 match.relevance = CalculateRelevance(input.type(), |
| 69 input.text().length(), |
| 70 matches_name ? |
| 71 name.length() : url.length(), |
| 72 GURL(url)); |
| 73 matches_.push_back(match); |
| 74 } |
| 75 } |
| 76 } |
| 77 } |
| 78 |
| 79 ExtensionAppProvider::~ExtensionAppProvider() { |
| 80 } |
| 81 |
| 82 void ExtensionAppProvider::RefreshAppList() { |
| 83 ExtensionService* extension_service = profile_->GetExtensionService(); |
| 84 if (!extension_service) |
| 85 return; // During testing, there is no extension service. |
| 86 const ExtensionList* extensions = extension_service->extensions(); |
| 87 extension_apps_.clear(); |
| 88 for (ExtensionList::const_iterator app = extensions->begin(); |
| 89 app != extensions->end(); ++app) { |
| 90 if ((*app)->is_app() && !(*app)->launch_web_url().empty()) { |
| 91 extension_apps_.push_back(std::make_pair((*app)->name(), |
| 92 (*app)->launch_web_url())); |
| 93 } |
| 94 } |
| 95 } |
| 96 |
| 97 void ExtensionAppProvider::RegisterForNotifications() { |
| 98 registrar_.Add(this, NotificationType::EXTENSION_LOADED, |
| 99 NotificationService::AllSources()); |
| 100 registrar_.Add(this, NotificationType::EXTENSION_UNINSTALLED, |
| 101 NotificationService::AllSources()); |
| 102 } |
| 103 |
| 104 void ExtensionAppProvider::Observe(NotificationType type, |
| 105 const NotificationSource& source, |
| 106 const NotificationDetails& details) { |
| 107 RefreshAppList(); |
| 108 } |
| 109 |
| 110 void ExtensionAppProvider::HighlightMatch(const AutocompleteInput& input, |
| 111 ACMatchClassifications* match_class, |
| 112 std::string::const_iterator iter, |
| 113 const std::string& match_string) { |
| 114 size_t pos = iter - match_string.begin(); |
| 115 bool match_found = iter != match_string.end(); |
| 116 if (!match_found || pos > 0) { |
| 117 match_class->push_back( |
| 118 ACMatchClassification(0, ACMatchClassification::DIM)); |
| 119 } |
| 120 if (match_found) { |
| 121 match_class->push_back( |
| 122 ACMatchClassification(pos, ACMatchClassification::MATCH)); |
| 123 if (pos + input.text().length() < match_string.length()) { |
| 124 match_class->push_back(ACMatchClassification(pos + input.text().length(), |
| 125 ACMatchClassification::DIM)); |
| 126 } |
| 127 } |
| 128 } |
| 129 |
| 130 int ExtensionAppProvider::CalculateRelevance(AutocompleteInput::Type type, |
| 131 int input_length, |
| 132 int target_length, |
| 133 const GURL& url) { |
| 134 // If you update the algorithm here, please remember to update the tables in |
| 135 // autocomplete.h also. |
| 136 const int kMaxRelevance = 1425; |
| 137 |
| 138 if (input_length == target_length) |
| 139 return kMaxRelevance; |
| 140 |
| 141 // We give a boost proportionally based on how much of the input matches the |
| 142 // app name, up to a maximum close to 200 (we can be close to, but we'll never |
| 143 // reach 200 because the 100% match is taken care of above). |
| 144 double fraction_boost = static_cast<double>(200) * |
| 145 input_length / target_length; |
| 146 |
| 147 // We also give a boost relative to how often the user has previously typed |
| 148 // the Extension App URL/selected the Extension App suggestion from this |
| 149 // provider (boost is between 200-400). |
| 150 double type_count_boost = 0; |
| 151 HistoryService* const history_service = |
| 152 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| 153 history::URLDatabase* url_db = history_service ? |
| 154 history_service->InMemoryDatabase() : NULL; |
| 155 if (url_db) { |
| 156 history::URLRow info; |
| 157 url_db->GetRowForURL(url, &info); |
| 158 type_count_boost = |
| 159 400 * (1.0 - (std::pow(static_cast<double>(2), -info.typed_count()))); |
| 160 } |
| 161 int relevance = 575 + static_cast<int>(type_count_boost) + |
| 162 static_cast<int>(fraction_boost); |
| 163 DCHECK_LE(relevance, kMaxRelevance); |
| 164 return relevance; |
| 165 } |
| OLD | NEW |