1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/autocomplete/history_url_provider.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/basictypes.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 11731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 14513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/browser/autocomplete/autocomplete_match.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history_backend.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history_database.h" 18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/history/history_types.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/net/url_fixer_upper.h" 203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/prefs/pref_service.h" 2121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/pref_names.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h" 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/url_parse.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/url_util.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_util.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::Time; 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::TimeDelta; 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::TimeTicks; 323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickusing history::Prefix; 333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickusing history::Prefixes; 343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickusing history::HistoryMatch; 353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickusing history::HistoryMatches; 363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merricknamespace history { 383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Returns true if |url| is just a host (e.g. "http://www.google.com/") and 403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// not some other subpage (e.g. "http://www.google.com/foo.html"). 413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool IsHostOnly(const GURL& url) { 423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK(url.is_valid()); 433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return (!url.has_path() || (url.path() == "/")) && !url.has_query() && 443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick !url.has_ref(); 453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Acts like the > operator for URLInfo classes. 483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool CompareHistoryMatch(const HistoryMatch& a, const HistoryMatch& b) { 493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // A URL that has been typed at all is better than one that has never been 503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // typed. (Note "!"s on each side) 513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!a.url_info.typed_count() != !b.url_info.typed_count()) 523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a.url_info.typed_count() > b.url_info.typed_count(); 533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Innermost matches (matches after any scheme or "www.") are better than 553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // non-innermost matches. 563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (a.innermost_match != b.innermost_match) 573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a.innermost_match; 583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // URLs that have been typed more often are better. 603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (a.url_info.typed_count() != b.url_info.typed_count()) 613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a.url_info.typed_count() > b.url_info.typed_count(); 623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // For URLs that have each been typed once, a host (alone) is better than a 643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // page inside. 653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (a.url_info.typed_count() == 1) { 663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const bool a_is_host_only = history::IsHostOnly(a.url_info.url()); 673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (a_is_host_only != history::IsHostOnly(b.url_info.url())) 683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a_is_host_only; 693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // URLs that have been visited more often are better. 723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (a.url_info.visit_count() != b.url_info.visit_count()) 733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a.url_info.visit_count() > b.url_info.visit_count(); 743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // URLs that have been visited more recently are better. 763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return a.url_info.last_visit() > b.url_info.last_visit(); 773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Given the user's |input| and a |match| created from it, reduce the 803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// match's URL to just a host. If this host still matches the user input, 813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// return it. Returns the empty string on failure. 8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenGURL ConvertToHostOnly(const HistoryMatch& match, const string16& input) { 833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // See if we should try to do host-only suggestions for this URL. Nonstandard 843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // schemes means there's no authority section, so suggesting the host name 853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // is useless. File URLs are standard, but host suggestion is not useful for 863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // them either. 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const GURL& url = match.url_info.url(); 883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!url.is_valid() || !url.IsStandard() || url.SchemeIsFile()) 893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return GURL(); 903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Transform to a host-only match. Bail if the host no longer matches the 923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // user input (e.g. because the user typed more than just a host). 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick GURL host = url.GetWithEmptyPath(); 943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if ((host.spec().length() < (match.input_location + input.length()))) 953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return GURL(); // User typing is longer than this host suggestion. 963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const string16 spec = UTF8ToUTF16(host.spec()); 983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (spec.compare(match.input_location, input.length(), input)) 993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return GURL(); // User typing is no longer a prefix. 1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return host; 1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} // namespace history 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochHistoryURLProviderParams::HistoryURLProviderParams( 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool trim_http, 109731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const std::string& languages) 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : message_loop(MessageLoop::current()), 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input(input), 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch trim_http(trim_http), 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cancel(false), 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch failed(false), 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen languages(languages), 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dont_suggest_exact_input(false) { 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 119731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickHistoryURLProviderParams::~HistoryURLProviderParams() {} 120731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickHistoryURLProvider::HistoryURLProvider(ACProviderListener* listener, 1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Profile* profile) 123731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : HistoryProvider(listener, profile, "HistoryURL"), 1243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick prefixes_(GetPrefixes()), 1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick params_(NULL) { 1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::Start(const AutocompleteInput& input, 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool minimal_changes) { 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: We could try hard to do less work in the |minimal_changes| case 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // here; some clever caching would let us reuse the raw matches from the 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // history DB without re-querying. However, we'd still have to go back to 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the history thread to mark these up properly, and if pass 2 is currently 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // running, we'd need to wait for it to return to the main thread before 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // doing this (we can't just write new data for it to read due to thread 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // safety issues). At that point it's just as fast, and easier, to simply 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // re-run the query from scratch and ignore |minimal_changes|. 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Cancel any in-progress query. 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Stop(); 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RunAutocompletePasses(input, true); 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::Stop() { 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = true; 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params_) 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_->cancel = true; 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called on the history thread. 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::ExecuteWithDB(history::HistoryBackend* backend, 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params) { 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We may get called with a NULL database if it couldn't be properly 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // initialized. 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db) { 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->failed = true; 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (!params->cancel) { 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch TimeTicks beginning_time = TimeTicks::Now(); 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DoAutocomplete(backend, db, params); 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UMA_HISTOGRAM_TIMES("Autocomplete.HistoryAsyncQueryTime", 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch TimeTicks::Now() - beginning_time); 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return the results (if any) to the main thread. 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->message_loop->PostTask(FROM_HERE, NewRunnableMethod( 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, &HistoryURLProvider::QueryComplete, params)); 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Used by both autocomplete passes, and therefore called on multiple different 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// threads (though not simultaneously). 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params) { 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create a What You Typed match, which we'll need below. 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We display this to the user when there's a reasonable chance they actually 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // care: 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * Their input can be opened as a URL, and 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * They hit ctrl-enter, or we parsed the input as a URL, or it starts with 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // an explicit "http:" or "https:". 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Otherwise, this is just low-quality noise. In the cases where we've parsed 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as UNKNOWN, we'll still show an accidental search infobar if need be. 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool have_what_you_typed_match = 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->input.canonicalized_url().is_valid() && 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (params->input.type() != AutocompleteInput::QUERY) && 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ((params->input.type() != AutocompleteInput::UNKNOWN) || 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !params->trim_http || 19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen url_util::FindAndCompareScheme(UTF16ToUTF8(params->input.text()), 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch chrome::kHttpsScheme, NULL)); 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch what_you_typed_match(SuggestExactInput(params->input, 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->trim_http)); 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get the matching URLs from the DB 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch typedef std::vector<history::URLRow> URLRowVector; 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch URLRowVector url_matches; 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches history_matches; 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick for (Prefixes::const_iterator i(prefixes_.begin()); i != prefixes_.end(); 2043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ++i) { 2053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (params->cancel) 2063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; // Canceled in the middle of a query, give up. 2073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We only need kMaxMatches results in the end, but before we get there we 2083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // need to promote lower-quality matches that are prefixes of 2093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // higher-quality matches, and remove lower-quality redirects. So we ask 2103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // for more results than we need, of every prefix type, in hopes this will 2113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // give us far more than enough to work with. CullRedirects() will then 2123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // reduce the list to the best kMaxMatches results. 21372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen db->AutocompleteForPrefix(i->prefix + params->input.text(), 214513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch kMaxMatches * 2, (backend == NULL), &url_matches); 2153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick for (URLRowVector::const_iterator j(url_matches.begin()); 2163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick j != url_matches.end(); ++j) { 21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const Prefix* best_prefix = BestPrefix(j->url(), string16()); 2183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK(best_prefix != NULL); 2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick history_matches.push_back(HistoryMatch(*j, i->prefix.length(), 2203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick !i->num_components, 2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick i->num_components >= best_prefix->num_components)); 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create sorted list of suggestions. 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CullPoorMatches(&history_matches); 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SortMatches(&history_matches); 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PromoteOrCreateShorterSuggestion(db, *params, have_what_you_typed_match, 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch what_you_typed_match, &history_matches); 230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to promote a match as an exact/inline autocomplete match. This also 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // moves it to the front of |history_matches|, so skip over it when 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // converting the rest of the matches. 234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t first_match = 1; 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t exact_suggestion = 0; 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Checking |is_history_what_you_typed_match| tells us whether 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // SuggestExactInput() succeeded in constructing a valid match. 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (what_you_typed_match.is_history_what_you_typed_match && 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen (!backend || !params->dont_suggest_exact_input) && 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FixupExactSuggestion(db, params->input, &what_you_typed_match, 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &history_matches)) { 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Got an exact match for the user's input. Treat it as the best match 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // regardless of the input type. 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch exact_suggestion = 1; 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(what_you_typed_match); 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (params->input.prevent_inline_autocomplete() || 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_matches.empty() || 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !PromoteMatchForInlineAutocomplete(params, history_matches.front())) { 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Failed to promote any URLs for inline autocompletion. Use the What You 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Typed match, if we have it. 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch first_match = 0; 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (have_what_you_typed_match) 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(what_you_typed_match); 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This is the end of the synchronous pass. 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!backend) 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove redirects and trim list to size. We want to provide up to 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // kMaxMatches results plus the What You Typed result, if it was added to 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |history_matches| above. 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CullRedirects(backend, &history_matches, kMaxMatches + exact_suggestion); 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Convert the history matches to autocomplete matches. 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = first_match; i < history_matches.size(); ++i) { 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match = history_matches[i]; 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!have_what_you_typed_match || 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (match.url_info.url() != 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL(params->matches.front().destination_url))); 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(HistoryMatchToACMatch(params, match, NORMAL, 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_matches.size() - 1 - i)); 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called on the main thread when the query is complete. 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::QueryComplete( 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params_gets_deleted) { 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Ensure |params_gets_deleted| gets deleted on exit. 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<HistoryURLProviderParams> params(params_gets_deleted); 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the user hasn't already started another query, clear our member pointer 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so we can't write into deleted memory. 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params_ == params_gets_deleted) 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_ = NULL; 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't send responses for queries that have been canceled. 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params->cancel) 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Already set done_ when we canceled, no need to set it again. 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't modify |matches_| if the query failed, since it might have a default 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // match in it, whereas |params->matches| will be empty. 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!params->failed) { 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.swap(params->matches); 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateStarredStateOfMatches(); 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = true; 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch listener_->OnProviderUpdate(true); 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompleteMatch HistoryURLProvider::SuggestExactInput( 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool trim_http) { 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch match(this, 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CalculateRelevance(input.type(), WHAT_YOU_TYPED, 0), false, 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::URL_WHAT_YOU_TYPED); 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url = input.canonicalized_url(); 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (url.is_valid()) { 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.destination_url = url; 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Trim off "http://" if the user didn't type it. 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: We use TrimHttpPrefix() here rather than StringForURLDisplay() to 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // strip the scheme as we need to know the offset so we can adjust the 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |match_location| below. StringForURLDisplay() and TrimHttpPrefix() have 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // slightly different behavior as well (the latter will strip even without 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // two slashes after the scheme). 31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen string16 display_string(StringForURLDisplay(url, false, false)); 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t offset = trim_http ? TrimHttpPrefix(&display_string) : 0; 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.fill_into_edit = 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteInput::FormattedStringWithEquivalentMeaning(url, 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch display_string); 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: Don't set match.input_location (to allow inline autocompletion) 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // here, it's surprising and annoying. 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to highlight "innermost" match location. If we fix up "w" into 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "www.w.com", we want to highlight the fifth character, not the first. 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This relies on match.destination_url being the non-prefix-trimmed version 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // of match.contents. 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents = display_string; 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const Prefix* best_prefix = BestPrefix(match.destination_url, input.text()); 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Because of the vagaries of GURL, it's possible for match.destination_url 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to not contain the user's input at all. In this case don't mark anything 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as a match. 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t match_location = (best_prefix == NULL) ? 33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen string16::npos : best_prefix->prefix.length() - offset; 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyLocationInString(match_location, 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input.text().length(), 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents.length(), 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::URL, 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.contents_class); 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.is_history_what_you_typed_match = true; 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return match; 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::FixupExactSuggestion(history::URLDatabase* db, 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch* match, 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches) const { 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(match != NULL); 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(matches != NULL); 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tricky corner case: The user has visited intranet site "foo", but not 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // internet site "www.foo.com". He types in foo (getting an exact match), 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // then tries to hit ctrl-enter. When pressing ctrl, the what-you-typed 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // match ("www.foo.com") doesn't show up in history, and thus doesn't get a 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // promoted relevance, but a different match from the input ("foo") does, and 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // gets promoted for inline autocomplete. Thus instead of getting 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "www.foo.com", the user still gets "foo" (and, before hitting enter, 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // probably gets an odd-looking inline autocomplete of "/"). 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We detect this crazy case as follows: 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * If the what-you-typed match is not in the history DB, 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * and the user has specified a TLD, 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * and the input _without_ the TLD _is_ in the history DB, 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * ...then just before pressing "ctrl" the best match we supplied was the 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // what-you-typed match, so stick with it by promoting this. 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLRow info; 3733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick MatchType type = INLINE_AUTOCOMPLETE; 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db->GetRowForURL(match->destination_url, &info)) { 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (input.desired_tld().empty()) 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen GURL destination_url(URLFixerUpper::FixupURL(UTF16ToUTF8(input.text()), 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string())); 3793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!db->GetRowForURL(destination_url, NULL)) 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 3813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // If we got here, then we hit the tricky corner case. Make sure that 3833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // |info| corresponds to the right URL. 3843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick info = history::URLRow(match->destination_url); 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We have data for this match, use it. 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match->deletable = true; 38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen match->description = info.title(); 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyMatchInString(input.text(), 39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen info.title(), 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::NONE, &match->description_class); 3923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!info.typed_count()) { 3933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // If we reach here, we must be in the second pass, and we must not have 3943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // promoted this match as an exact match during the first pass. That 3953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // means it will have been outscored by the "search what you typed match". 3963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We need to maintain that ordering in order to not make the destination 3973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // for the user's typing change depending on when they hit enter. So 3983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // lower the score here enough to let the search provider continue to 3993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // outscore this match. 4003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick type = WHAT_YOU_TYPED; 4013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote as an exact match. 4053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick match->relevance = CalculateRelevance(input.type(), type, 0); 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Put it on the front of the HistoryMatches for redirect culling. 40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen EnsureMatchPresent(info, string16::npos, false, matches, true); 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::PromoteMatchForInlineAutocomplete( 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params, 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match) { 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote the first match if it's been typed at least n times, where n == 1 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for these long URLs because it's less likely that users will want to visit 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // them again. Even though we don't increment the typed_count for pasted-in 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // URLs, if the user manually edits the URL or types some long thing in by 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // hand, we wouldn't want to immediately start autocompleting it. 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!match.url_info.typed_count() || 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ((match.url_info.typed_count() == 1) && 4233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick !history::IsHostOnly(match.url_info.url()))) 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // In the case where the user has typed "foo.com" and visited (but not typed) 427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // "foo/", and the input is "foo", we can reach here for "foo.com" during the 428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // first pass but have the second pass suggest the exact input as a better 429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // URL. Since we need both passes to agree, and since during the first pass 430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // there's no way to know about "foo/", make reaching this point prevent any 431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // future pass from suggesting the exact input as a better match. 432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params->dont_suggest_exact_input = true; 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(HistoryMatchToACMatch(params, match, 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch INLINE_AUTOCOMPLETE, 0)); 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 43872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHistoryURLProvider::~HistoryURLProvider() {} 43972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 4413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickhistory::Prefixes HistoryURLProvider::GetPrefixes() { 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We'll complete text following these prefixes. 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: There's no requirement that these be in any particular order. 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Prefixes prefixes; 44572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("https://www."), 2)); 44672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("http://www."), 2)); 44772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("ftp://ftp."), 2)); 44872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("ftp://www."), 2)); 44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("https://"), 1)); 45072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("http://"), 1)); 45172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(ASCIIToUTF16("ftp://"), 1)); 45272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Empty string catches within-scheme matches as well. 45372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefixes.push_back(Prefix(string16(), 0)); 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return prefixes; 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint HistoryURLProvider::CalculateRelevance(AutocompleteInput::Type input_type, 459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MatchType match_type, 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_number) { 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch switch (match_type) { 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case INLINE_AUTOCOMPLETE: 463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 1400; 464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case WHAT_YOU_TYPED: 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 1200; 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default: 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 900 + static_cast<int>(match_number); 470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::PromoteOrCreateShorterSuggestion( 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryURLProviderParams& params, 477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool have_what_you_typed_match, 478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteMatch& what_you_typed_match, 479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches) { 480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (matches->empty()) 481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // No matches, nothing to do. 482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Determine the base URL from which to search, and whether that URL could 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // itself be added as a match. We can add the base iff it's not "effectively 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the same" as any "what you typed" match. 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match = matches->front(); 4873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick GURL search_base = history::ConvertToHostOnly(match, params.input.text()); 488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool can_add_search_base_to_matches = !have_what_you_typed_match; 489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base.is_empty()) { 490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Search from what the user typed when we couldn't reduce the best match 491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to a host. Careful: use a substring of |match| here, rather than the 492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // first match in |params|, because they might have different prefixes. If 493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the user typed "google.com", |what_you_typed_match| will hold 494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "http://google.com/", but |match| might begin with 495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "http://www.google.com/". 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO: this should be cleaned up, and is probably incorrect for IDN. 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string new_match = match.url_info.url().possibly_invalid_spec(). 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch substr(0, match.input_location + params.input.text().length()); 499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch search_base = GURL(new_match); 5003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // TODO(mrossetti): There is a degenerate case where the following may 5013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // cause a failure: http://www/~someword/fubar.html. Diagnose. 5023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // See: http://crbug.com/50101 503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base.is_empty()) 504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Can't construct a valid URL from which to start a search. 505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (!can_add_search_base_to_matches) { 506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch can_add_search_base_to_matches = 507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (search_base != what_you_typed_match.destination_url); 508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base == match.url_info.url()) 510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Couldn't shorten |match|, so no range of URLs to search over. 511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Search the DB for short URLs between our base and |match|. 513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLRow info(search_base); 514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool promote = true; 515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // A short URL is only worth suggesting if it's been visited at least a third 516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as often as the longer URL. 517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int min_visit_count = ((match.url_info.visit_count() - 1) / 3) + 1; 518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // For stability between the in-memory and on-disk autocomplete passes, when 519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the long URL has been typed before, only suggest shorter URLs that have 520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // also been typed. Otherwise, the on-disk pass could suggest a shorter URL 521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (which hasn't been typed) that the in-memory pass doesn't know about, 522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // thereby making the top match, and thus the behavior of inline 523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete, unstable. 524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int min_typed_count = match.url_info.typed_count() ? 1 : 0; 525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), 526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.url_info.url().possibly_invalid_spec(), min_visit_count, 527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch min_typed_count, can_add_search_base_to_matches, &info)) { 528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!can_add_search_base_to_matches) 529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Couldn't find anything and can't add the search base, bail. 530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to get info on the search base itself. Promote it to the top if the 532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // original best match isn't good enough to autocomplete. 533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch db->GetRowForURL(search_base, &info); 534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch promote = match.url_info.typed_count() <= 1; 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote or add the desired URL to the list of matches. 538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EnsureMatchPresent(info, match.input_location, match.match_in_scheme, 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches, promote); 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::EnsureMatchPresent( 544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const history::URLRow& info, 54572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen string16::size_type input_location, 546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool match_in_scheme, 547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool promote) { 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |matches| may already have an entry for this. 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (HistoryMatches::iterator i(matches->begin()); i != matches->end(); 551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i->url_info.url() == info.url()) { 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Rotate it to the front if the caller wishes. 554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (promote) 555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::rotate(matches->begin(), i, i + 1); 556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // No entry, so create one. 561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatch match(info, input_location, match_in_scheme, true); 562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (promote) 563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->push_front(match); 564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->push_back(match); 566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::RunAutocompletePasses( 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool fixup_input_and_run_pass_1) { 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.clear(); 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((input.type() == AutocompleteInput::INVALID) || 574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (input.type() == AutocompleteInput::FORCED_QUERY)) 575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create a match for exactly what the user typed. This will only be used as 578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // a fallback in case we can't get the history service or URL DB; otherwise, 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we'll run this again in DoAutocomplete() and use that result instead. 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const bool trim_http = !HasHTTPScheme(input.text()); 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't do this for queries -- while we can sometimes mark up a match for 582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this, it's not what the user wants, and just adds noise. 583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((input.type() != AutocompleteInput::QUERY) && 584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input.canonicalized_url().is_valid()) 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.push_back(SuggestExactInput(input, trim_http)); 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We'll need the history service to run both passes, so try to obtain it. 588ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!profile_) 589ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryService* const history_service = 591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); 592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!history_service) 593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create the data structure for the autocomplete passes. We'll save this off 596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // onto the |params_| member for later deletion below if we need to run pass 597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. 598731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string languages(languages_); 599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (languages.empty()) { 600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch languages = 601731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); 602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<HistoryURLProviderParams> params( 604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new HistoryURLProviderParams(input, trim_http, languages)); 605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (fixup_input_and_run_pass_1) { 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Do some fixup on the user input before matching against it, so we provide 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // good results for local file paths, input with spaces, etc. 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: This purposefully doesn't take input.desired_tld() into account; if 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // it did, then holding "ctrl" would change all the results from the 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HistoryURLProvider provider, not just the What You Typed Result. 61272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const string16 fixed_text(FixupUserInput(input)); 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (fixed_text.empty()) { 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Conceivably fixup could result in an empty string (although I don't 615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // have cases where this happens offhand). We can't do anything with 616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // empty input, so just bail; otherwise we'd crash later. 617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->input.set_text(fixed_text); 620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 1: Get the in-memory URL database, and use it to find and promote 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the inline autocomplete match, if any. 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* url_db = history_service->InMemoryDatabase(); 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // url_db can be NULL if it hasn't finished initializing (or failed to 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // initialize). In this case all we can do is fall back on the second 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // pass. 627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(pkasting): We should just block here until this loads. Any time 629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // someone unloads the history backend, we'll get inconsistent inline 630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete behavior here. 631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (url_db) { 632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DoAutocomplete(NULL, url_db, params.get()); 633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // params->matches now has the matches we should expose to the provider. 634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 2 expects a "clean slate" set of matches. 635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.clear(); 636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.swap(params->matches); 637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateStarredStateOfMatches(); 638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 2: Ask the history service to call us back on the history thread, 642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // where we can read the full on-disk DB. 643ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (input.matches_requested() == AutocompleteInput::ALL_MATCHES) { 644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = false; 645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_ = params.release(); // This object will be destroyed in 646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // QueryComplete() once we're done with it. 647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_service->ScheduleAutocomplete(this, params_); 648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 6513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst history::Prefix* HistoryURLProvider::BestPrefix( 652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url, 65372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const string16& prefix_suffix) const { 654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const Prefix* best_prefix = NULL; 65572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const string16 text(UTF8ToUTF16(url.spec())); 656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (Prefixes::const_iterator i(prefixes_.begin()); i != prefixes_.end(); 657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((best_prefix == NULL) || 659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (i->num_components > best_prefix->num_components)) { 66072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen string16 prefix_with_suffix(i->prefix + prefix_suffix); 661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((text.length() >= prefix_with_suffix.length()) && 662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !text.compare(0, prefix_with_suffix.length(), prefix_with_suffix)) 663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch best_prefix = &(*i); 664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return best_prefix; 667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::SortMatches(HistoryMatches* matches) const { 670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Sort by quality, best first. 6713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick std::sort(matches->begin(), matches->end(), &history::CompareHistoryMatch); 672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove duplicate matches (caused by the search string appearing in one of 674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the prefixes as well as after it). Consider the following scenario: 675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // User has visited "http://http.com" once and "http://htaccess.com" twice. 677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // User types "http". The autocomplete search with prefix "http://" returns 678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the first host, while the search with prefix "" returns both hosts. Now 679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we sort them into rank order: 680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://http.com (innermost_match) 681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://htaccess.com (!innermost_match, url_info.visit_count == 2) 682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://http.com (!innermost_match, url_info.visit_count == 1) 683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The above scenario tells us we can't use std::unique(), since our 685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // duplicates are not always sequential. It also tells us we should remove 686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the lower-quality duplicate(s), since otherwise the returned results won't 687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be ordered correctly. This is easy to do: we just always remove the later 688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // element of a duplicate pair. 689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Be careful! Because the vector contents may change as we remove elements, 690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we use an index instead of an iterator in the outer loop, and don't 691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // precalculate the ending position. 692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < matches->size(); ++i) { 693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator j(matches->begin() + i + 1); 694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (j != matches->end()) { 695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((*matches)[i].url_info.url() == j->url_info.url()) 696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch j = matches->erase(j); 697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++j; 699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::CullPoorMatches(HistoryMatches* matches) const { 704dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const base::Time& threshold(history::AutocompleteAgeThreshold()); 705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (HistoryMatches::iterator i(matches->begin()); i != matches->end();) { 706dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (RowQualifiesAsSignificant(i->url_info, threshold)) 707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i; 708dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen else 709dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen i = matches->erase(i); 710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::CullRedirects(history::HistoryBackend* backend, 714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t max_results) const { 716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t source = 0; 717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (source < matches->size()) && (source < max_results); ) { 718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url = (*matches)[source].url_info.url(); 719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(brettw) this should go away when everything uses GURL. 720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::RedirectList redirects; 721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch backend->GetMostRecentRedirectsFrom(url, &redirects); 722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!redirects.empty()) { 723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove all but the first occurrence of any of these redirects in the 724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // search results. We also must add the URL we queried for, since it may 725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // not be the first match and we'd want to remove it. 726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // For example, when A redirects to B and our matches are [A, X, B], 728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we'll get B as the redirects from, and we want to remove the second 729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // item of that pair, removing B. If A redirects to B and our matches are 730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // [B, X, A], we'll want to remove A instead. 731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirects.push_back(url); 732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source = RemoveSubsequentMatchesOf(matches, source, redirects); 733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Advance to next item. 735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source++; 736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (matches->size() > max_results) 740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->resize(max_results); 741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t HistoryURLProvider::RemoveSubsequentMatchesOf( 744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t source_index, 746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::vector<GURL>& remove) const { 747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t next_index = source_index + 1; // return value = item after source 748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Find the first occurrence of any URL in the redirect chain. We want to 750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // keep this one since it is rated the highest. 751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator first(std::find_first_of( 752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->begin(), matches->end(), remove.begin(), remove.end())); 753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(first != matches->end()) << 754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "We should have always found at least the original URL."; 755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Find any following occurrences of any URL in the redirect chain, these 757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // should be deleted. 758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator next(first); 759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next++; // Start searching immediately after the one we found already. 760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (next != matches->end() && 761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (next = std::find_first_of(next, matches->end(), remove.begin(), 762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch remove.end())) != matches->end()) { 763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove this item. When we remove an item before the source index, we 764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // need to shift it to the right and remember that so we can return it. 765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next = matches->erase(next); 766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (static_cast<size_t>(next - matches->begin()) < next_index) 767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next_index--; 768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return next_index; 770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 772c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( 773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params, 774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& history_match, 775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MatchType match_type, 776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_number) { 777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const history::URLRow& info = history_match.url_info; 778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch match(this, 779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CalculateRelevance(params->input.type(), match_type, match_number), 780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !!info.visit_count(), AutocompleteMatch::HISTORY_URL); 781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.destination_url = info.url(); 782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(match.destination_url.is_valid()); 783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t inline_autocomplete_offset = 784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_match.input_location + params->input.text().length(); 7853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick std::string languages = (match_type == WHAT_YOU_TYPED) ? 786731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string() : params->languages; 787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & 788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ~((params->trim_http && !history_match.match_in_scheme) ? 789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 0 : net::kFormatUrlOmitHTTP); 790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.fill_into_edit = 791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), 79272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen net::FormatUrl(info.url(), languages, format_types, 79372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UnescapeRule::SPACES, NULL, NULL, 79472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen &inline_autocomplete_offset)); 795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!params->input.prevent_inline_autocomplete()) 796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.inline_autocomplete_offset = inline_autocomplete_offset; 79772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK((match.inline_autocomplete_offset == string16::npos) || 798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (match.inline_autocomplete_offset <= match.fill_into_edit.length())); 799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_start = history_match.input_location; 80172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen match.contents = net::FormatUrl(info.url(), languages, 80272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen format_types, UnescapeRule::SPACES, NULL, NULL, &match_start); 80372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((match_start != string16::npos) && 80472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen (inline_autocomplete_offset != string16::npos) && 805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (inline_autocomplete_offset != match_start)) { 806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(inline_autocomplete_offset > match_start); 807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyLocationInString(match_start, 808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch inline_autocomplete_offset - match_start, match.contents.length(), 809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::URL, &match.contents_class); 810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 81172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen AutocompleteMatch::ClassifyLocationInString(string16::npos, 0, 812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents.length(), ACMatchClassification::URL, 813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.contents_class); 814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 81572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen match.description = info.title(); 816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyMatchInString(params->input.text(), 81772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen info.title(), 818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::NONE, 819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.description_class); 820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return match; 822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 823