summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/autocomplete/autocomplete.cc2
-rw-r--r--chrome/browser/autocomplete/autocomplete.h15
-rw-r--r--chrome/browser/autocomplete/autocomplete_browsertest.cc5
-rw-r--r--chrome/browser/autocomplete/autocomplete_match.h10
-rw-r--r--chrome/browser/autocomplete/extension_app_provider.cc165
-rw-r--r--chrome/browser/autocomplete/extension_app_provider.h83
-rw-r--r--chrome/chrome_browser.gypi2
7 files changed, 275 insertions, 7 deletions
diff --git a/chrome/browser/autocomplete/autocomplete.cc b/chrome/browser/autocomplete/autocomplete.cc
index b076dc0..c022f96 100644
--- a/chrome/browser/autocomplete/autocomplete.cc
+++ b/chrome/browser/autocomplete/autocomplete.cc
@@ -16,6 +16,7 @@
#include "chrome/browser/autocomplete/autocomplete_controller_delegate.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/autocomplete/builtin_provider.h"
+#include "chrome/browser/autocomplete/extension_app_provider.h"
#include "chrome/browser/autocomplete/history_contents_provider.h"
#include "chrome/browser/autocomplete/history_quick_provider.h"
#include "chrome/browser/autocomplete/history_url_provider.h"
@@ -796,6 +797,7 @@ AutocompleteController::AutocompleteController(
providers_.push_back(new KeywordProvider(this, profile));
providers_.push_back(new HistoryContentsProvider(this, profile));
providers_.push_back(new BuiltinProvider(this, profile));
+ providers_.push_back(new ExtensionAppProvider(this, profile));
for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
(*i)->AddRef();
}
diff --git a/chrome/browser/autocomplete/autocomplete.h b/chrome/browser/autocomplete/autocomplete.h
index d1809d3..95b1ee35 100644
--- a/chrome/browser/autocomplete/autocomplete.h
+++ b/chrome/browser/autocomplete/autocomplete.h
@@ -43,10 +43,12 @@
// UNKNOWN input type:
// --------------------------------------------------------------------|-----
// Keyword (non-substituting or in keyword UI mode, exact match) | 1500
+// Extension App (exact match) | 1425
// HistoryURL (exact or inline autocomplete match) | 1400
// Search Primary Provider (past query in history within 2 days) | 1399**
// Search Primary Provider (what you typed) | 1300
// HistoryURL (what you typed) | 1200
+// Extension App (inexact match) | 1175*~
// Keyword (substituting, exact match) | 1100
// Search Primary Provider (past query in history older than 2 days) | 1050--
// HistoryContents (any match in title of starred page) | 1000++
@@ -66,9 +68,11 @@
// REQUESTED_URL input type:
// --------------------------------------------------------------------|-----
// Keyword (non-substituting or in keyword UI mode, exact match) | 1500
+// Extension App (exact match) | 1425
// HistoryURL (exact or inline autocomplete match) | 1400
// Search Primary Provider (past query in history within 2 days) | 1399**
// HistoryURL (what you typed) | 1200
+// Extension App (inexact match) | 1175*~
// Search Primary Provider (what you typed) | 1150
// Keyword (substituting, exact match) | 1100
// Search Primary Provider (past query in history older than 2 days) | 1050--
@@ -89,8 +93,10 @@
// URL input type:
// --------------------------------------------------------------------|-----
// Keyword (non-substituting or in keyword UI mode, exact match) | 1500
+// Extension App (exact match) | 1425
// HistoryURL (exact or inline autocomplete match) | 1400
// HistoryURL (what you typed) | 1200
+// Extension App (inexact match) | 1175*~
// Keyword (substituting, exact match) | 1100
// HistoryURL (inexact match) | 900++
// Search Primary Provider (what you typed) | 850
@@ -108,9 +114,11 @@
// --------------------------------------------------------------------|-----
// Keyword (non-substituting or in keyword UI mode, exact match) | 1500
// Keyword (substituting, exact match) | 1450
+// Extension App (exact match) | 1425
// HistoryURL (exact or inline autocomplete match) | 1400
// Search Primary Provider (past query in history within 2 days) | 1399**
// Search Primary Provider (what you typed) | 1300
+// Extension App (inexact match) | 1175*~
// Search Primary Provider (past query in history older than 2 days) | 1050--
// HistoryContents (any match in title of starred page) | 1000++
// HistoryURL (inexact match) | 900++
@@ -127,8 +135,10 @@
//
// FORCED_QUERY input type:
// --------------------------------------------------------------------|-----
+// Extension App (exact match on title only, not url) | 1425
// Search Primary Provider (past query in history within 2 days) | 1399**
// Search Primary Provider (what you typed) | 1300
+// Extension App (inexact match on title only, not url) | 1175*~
// Search Primary Provider (past query in history older than 2 days) | 1050--
// HistoryContents (any match in title of starred page) | 1000++
// Search Primary Provider (navigational suggestion) | 800++
@@ -149,8 +159,11 @@
// ++: a series of matches with relevance from n up to (n + max_matches).
// --: relevance score falls off over time (discounted 50 points @ 15 minutes,
// 450 points @ two weeks)
-// --: relevance score falls off over two days (discounted 99 points after two
+// **: relevance score falls off over two days (discounted 99 points after two
// days).
+// *~: Partial matches get a score on a sliding scale from about 575-1125 based
+// on how many times the URL for the Extension App has been typed and how
+// many of the letters match.
class AutocompleteController;
class AutocompleteControllerDelegate;
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc
index b115277..edb02d4 100644
--- a/chrome/browser/autocomplete/autocomplete_browsertest.cc
+++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc
@@ -115,7 +115,10 @@ IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, MAYBE_Autocomplete) {
EXPECT_TRUE(location_bar->location_entry()->GetText().empty());
EXPECT_TRUE(location_bar->location_entry()->IsSelectAll());
const AutocompleteResult& result = autocomplete_controller->result();
- ASSERT_EQ(1U, result.size()) << AutocompleteResultAsString(result);
+ // We get two matches because we have a provider for extension apps and the
+ // Chrome Web Store is a built-in Extension app. For this test, we only care
+ // about the other match existing.
+ ASSERT_GE(result.size(), 1U) << AutocompleteResultAsString(result);
AutocompleteMatch match = result.match_at(0);
EXPECT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, match.type);
EXPECT_FALSE(match.deletable);
diff --git a/chrome/browser/autocomplete/autocomplete_match.h b/chrome/browser/autocomplete/autocomplete_match.h
index a376062..3067715 100644
--- a/chrome/browser/autocomplete/autocomplete_match.h
+++ b/chrome/browser/autocomplete/autocomplete_match.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -128,10 +128,10 @@ struct AutocompleteMatch {
// no provider (or memory of the user's selection).
AutocompleteProvider* provider;
- // The relevance of this match. See table above for scores returned by
- // various providers. This is used to rank matches among all responding
- // providers, so different providers must be carefully tuned to supply
- // matches with appropriate relevance.
+ // The relevance of this match. See table in autocomplete.h for scores
+ // returned by various providers. This is used to rank matches among all
+ // responding providers, so different providers must be carefully tuned to
+ // supply matches with appropriate relevance.
//
// TODO(pkasting): http://b/1111299 This should be calculated algorithmically,
// rather than being a fairly fixed value defined by the table above.
diff --git a/chrome/browser/autocomplete/extension_app_provider.cc b/chrome/browser/autocomplete/extension_app_provider.cc
new file mode 100644
index 0000000..eff2bb1
--- /dev/null
+++ b/chrome/browser/autocomplete/extension_app_provider.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2011 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/autocomplete/extension_app_provider.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/history/url_database.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/common/notification_service.h"
+#include "ui/base/l10n/l10n_util.h"
+
+ExtensionAppProvider::ExtensionAppProvider(ACProviderListener* listener,
+ Profile* profile)
+ : AutocompleteProvider(listener, profile, "ExtensionApps") {
+ RegisterForNotifications();
+ RefreshAppList();
+}
+
+void ExtensionAppProvider::Start(const AutocompleteInput& input,
+ bool minimal_changes) {
+ matches_.clear();
+
+ if (input.type() == AutocompleteInput::INVALID)
+ return;
+
+ if (!input.text().empty()) {
+ std::string input_utf8 = UTF16ToUTF8(input.text());
+ for (ExtensionApps::const_iterator app = extension_apps_.begin();
+ app != extension_apps_.end(); ++app) {
+ // See if the input matches this extension application.
+ const std::string& name = app->first;
+ const std::string& url = app->second;
+ std::string::const_iterator name_iter =
+ std::search(name.begin(),
+ name.end(),
+ input_utf8.begin(),
+ input_utf8.end(),
+ base::CaseInsensitiveCompare<char>());
+ std::string::const_iterator url_iter =
+ std::search(url.begin(),
+ url.end(),
+ input_utf8.begin(),
+ input_utf8.end(),
+ base::CaseInsensitiveCompare<char>());
+
+ bool matches_name = name_iter != name.end();
+ bool matches_url = url_iter != url.end() &&
+ input.type() != AutocompleteInput::FORCED_QUERY;
+ if (matches_name || matches_url) {
+ // We have a match, might be a partial match.
+ // TODO(finnur): Figure out what type to return here, might want to have
+ // the extension icon/a generic icon show up in the Omnibox.
+ AutocompleteMatch match(this, 0, false, AutocompleteMatch::HISTORY_URL);
+ match.fill_into_edit = UTF8ToUTF16(url);
+ match.destination_url = GURL(url);
+ match.inline_autocomplete_offset = string16::npos;
+ match.contents = UTF8ToUTF16(name);
+ HighlightMatch(input, &match.contents_class, name_iter, name);
+ match.description = UTF8ToUTF16(url);
+ HighlightMatch(input, &match.description_class, url_iter, url);
+ match.relevance = CalculateRelevance(input.type(),
+ input.text().length(),
+ matches_name ?
+ name.length() : url.length(),
+ GURL(url));
+ matches_.push_back(match);
+ }
+ }
+ }
+}
+
+ExtensionAppProvider::~ExtensionAppProvider() {
+}
+
+void ExtensionAppProvider::RefreshAppList() {
+ ExtensionService* extension_service = profile_->GetExtensionService();
+ if (!extension_service)
+ return; // During testing, there is no extension service.
+ const ExtensionList* extensions = extension_service->extensions();
+ extension_apps_.clear();
+ for (ExtensionList::const_iterator app = extensions->begin();
+ app != extensions->end(); ++app) {
+ if ((*app)->is_app() && !(*app)->launch_web_url().empty()) {
+ extension_apps_.push_back(std::make_pair((*app)->name(),
+ (*app)->launch_web_url()));
+ }
+ }
+}
+
+void ExtensionAppProvider::RegisterForNotifications() {
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UNINSTALLED,
+ NotificationService::AllSources());
+}
+
+void ExtensionAppProvider::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ RefreshAppList();
+}
+
+void ExtensionAppProvider::HighlightMatch(const AutocompleteInput& input,
+ ACMatchClassifications* match_class,
+ std::string::const_iterator iter,
+ const std::string& match_string) {
+ size_t pos = iter - match_string.begin();
+ bool match_found = iter != match_string.end();
+ if (!match_found || pos > 0) {
+ match_class->push_back(
+ ACMatchClassification(0, ACMatchClassification::DIM));
+ }
+ if (match_found) {
+ match_class->push_back(
+ ACMatchClassification(pos, ACMatchClassification::MATCH));
+ if (pos + input.text().length() < match_string.length()) {
+ match_class->push_back(ACMatchClassification(pos + input.text().length(),
+ ACMatchClassification::DIM));
+ }
+ }
+}
+
+int ExtensionAppProvider::CalculateRelevance(AutocompleteInput::Type type,
+ int input_length,
+ int target_length,
+ const GURL& url) {
+ // If you update the algorithm here, please remember to update the tables in
+ // autocomplete.h also.
+ const int kMaxRelevance = 1425;
+
+ if (input_length == target_length)
+ return kMaxRelevance;
+
+ // We give a boost proportionally based on how much of the input matches the
+ // app name, up to a maximum close to 200 (we can be close to, but we'll never
+ // reach 200 because the 100% match is taken care of above).
+ double fraction_boost = static_cast<double>(200) *
+ input_length / target_length;
+
+ // We also give a boost relative to how often the user has previously typed
+ // the Extension App URL/selected the Extension App suggestion from this
+ // provider (boost is between 200-400).
+ double type_count_boost = 0;
+ HistoryService* const history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ history::URLDatabase* url_db = history_service ?
+ history_service->InMemoryDatabase() : NULL;
+ if (url_db) {
+ history::URLRow info;
+ url_db->GetRowForURL(url, &info);
+ type_count_boost =
+ 400 * (1.0 - (std::pow(static_cast<double>(2), -info.typed_count())));
+ }
+ int relevance = 575 + static_cast<int>(type_count_boost) +
+ static_cast<int>(fraction_boost);
+ DCHECK_LE(relevance, kMaxRelevance);
+ return relevance;
+}
diff --git a/chrome/browser/autocomplete/extension_app_provider.h b/chrome/browser/autocomplete/extension_app_provider.h
new file mode 100644
index 0000000..a162419
--- /dev/null
+++ b/chrome/browser/autocomplete/extension_app_provider.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 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.
+//
+//
+// This file contains the Extension App autocomplete provider. The provider
+// is responsible for keeping track of which Extension Apps are installed and
+// their URLs. An instance of it gets created and managed by the autocomplete
+// controller.
+//
+// For more information on the autocomplete system in general, including how
+// the autocomplete controller and autocomplete providers work, see
+// chrome/browser/autocomplete.h.
+
+#ifndef CHROME_BROWSER_AUTOCOMPLETE_EXTENSION_APP_PROVIDER_H_
+#define CHROME_BROWSER_AUTOCOMPLETE_EXTENSION_APP_PROVIDER_H_
+#pragma once
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/autocomplete/autocomplete.h"
+#include "chrome/browser/autocomplete/autocomplete_match.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+
+class TemplateURLModel;
+
+class ExtensionAppProvider : public AutocompleteProvider,
+ public NotificationObserver {
+ public:
+ ExtensionAppProvider(ACProviderListener* listener, Profile* profile);
+
+ // AutocompleteProvider implementation:
+ virtual void Start(const AutocompleteInput& input,
+ bool minimal_changes) OVERRIDE;
+
+ private:
+ // An ExtensionApp is a pair of Extension Name and the Launch URL.
+ typedef std::pair<std::string, std::string> ExtensionApp;
+ typedef std::vector<ExtensionApp> ExtensionApps;
+
+ virtual ~ExtensionAppProvider();
+
+ // Fetch the current app list and cache it locally.
+ void RefreshAppList();
+
+ // Register for install/uninstall notification so we can update our cache.
+ void RegisterForNotifications();
+
+ // Highlights a certain part of a match string within a certain match class.
+ // |input| is the input we got from the user, |match_class| is the
+ // AutoComplete match classification that keeps track of the highlighting
+ // values, and |iter| is the location of the user input found within
+ // |match_string|.
+ void HighlightMatch(const AutocompleteInput& input,
+ ACMatchClassifications* match_class,
+ std::string::const_iterator iter,
+ const std::string& match_string);
+
+ // Calculate the relevance of the match.
+ int CalculateRelevance(AutocompleteInput::Type type,
+ int input_length,
+ int target_length,
+ const GURL& url);
+
+ // NotificationObserver implementation:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ NotificationRegistrar registrar_;
+
+ // Our cache of ExtensionApp objects (name + url) representing the extension
+ // apps we know about.
+ ExtensionApps extension_apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionAppProvider);
+};
+
+#endif // CHROME_BROWSER_AUTOCOMPLETE_EXTENSION_APP_PROVIDER_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 7e3187d..5d8a49e 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -126,6 +126,8 @@
'browser/autocomplete/autocomplete_popup_view_mac.mm',
'browser/autocomplete/builtin_provider.cc',
'browser/autocomplete/builtin_provider.h',
+ 'browser/autocomplete/extension_app_provider.cc',
+ 'browser/autocomplete/extension_app_provider.h',
'browser/autocomplete/history_contents_provider.cc',
'browser/autocomplete/history_contents_provider.h',
'browser/autocomplete/history_provider.cc',