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