history_url_provider.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 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/command_line.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/histogram.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.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" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/net/url_fixer_upper.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/pref_service.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/profile.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.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; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochHistoryURLProviderParams::HistoryURLProviderParams( 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool trim_http, 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& languages) 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : message_loop(MessageLoop::current()), 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input(input), 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch trim_http(trim_http), 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cancel(false), 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch failed(false), 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch languages(languages) { 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::Start(const AutocompleteInput& input, 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool minimal_changes) { 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: We could try hard to do less work in the |minimal_changes| case 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // here; some clever caching would let us reuse the raw matches from the 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // history DB without re-querying. However, we'd still have to go back to 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the history thread to mark these up properly, and if pass 2 is currently 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // running, we'd need to wait for it to return to the main thread before 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // doing this (we can't just write new data for it to read due to thread 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // safety issues). At that point it's just as fast, and easier, to simply 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // re-run the query from scratch and ignore |minimal_changes|. 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Cancel any in-progress query. 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Stop(); 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RunAutocompletePasses(input, true); 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::Stop() { 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = true; 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params_) 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_->cancel = true; 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::DeleteMatch(const AutocompleteMatch& match) { 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(done_); 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Delete the match from the history DB. 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryService* const history_service = 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL selected_url(match.destination_url); 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!history_service || !selected_url.is_valid()) { 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED() << "Can't delete requested URL"; 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_service->DeleteURL(selected_url); 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Delete the match from the current set of matches. 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool found = false; 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (ACMatches::iterator i(matches_.begin()); i != matches_.end(); ++i) { 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i->destination_url == match.destination_url) { 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch found = true; 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i->is_history_what_you_typed_match) { 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We can't get rid of the What You Typed match, but we can make it 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // look like it has no backing data. 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i->deletable = false; 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i->description.clear(); 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i->description_class.clear(); 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.erase(i); 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(found) << "Asked to delete a URL that isn't in our set of matches"; 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch listener_->OnProviderUpdate(true); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called on the history thread. 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::ExecuteWithDB(history::HistoryBackend* backend, 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params) { 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We may get called with a NULL database if it couldn't be properly 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // initialized. 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db) { 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->failed = true; 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (!params->cancel) { 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch TimeTicks beginning_time = TimeTicks::Now(); 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DoAutocomplete(backend, db, params); 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UMA_HISTOGRAM_TIMES("Autocomplete.HistoryAsyncQueryTime", 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch TimeTicks::Now() - beginning_time); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return the results (if any) to the main thread. 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->message_loop->PostTask(FROM_HERE, NewRunnableMethod( 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, &HistoryURLProvider::QueryComplete, params)); 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Used by both autocomplete passes, and therefore called on multiple different 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// threads (though not simultaneously). 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params) { 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create a What You Typed match, which we'll need below. 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We display this to the user when there's a reasonable chance they actually 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // care: 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * Their input can be opened as a URL, and 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * They hit ctrl-enter, or we parsed the input as a URL, or it starts with 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // an explicit "http:" or "https:". 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Otherwise, this is just low-quality noise. In the cases where we've parsed 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as UNKNOWN, we'll still show an accidental search infobar if need be. 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool have_what_you_typed_match = 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->input.canonicalized_url().is_valid() && 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (params->input.type() != AutocompleteInput::QUERY) && 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ((params->input.type() != AutocompleteInput::UNKNOWN) || 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !params->trim_http || 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch url_util::FindAndCompareScheme(WideToUTF8(params->input.text()), 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch chrome::kHttpsScheme, NULL)); 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch what_you_typed_match(SuggestExactInput(params->input, 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->trim_http)); 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get the matching URLs from the DB 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch typedef std::vector<history::URLRow> URLRowVector; 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch URLRowVector url_matches; 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches history_matches; 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (CommandLine::ForCurrentProcess()->HasSwitch( 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch switches::kEnableInMemoryURLIndex)) { 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(rohitrao): Fetch results from the index. 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (Prefixes::const_iterator i(prefixes_.begin()); i != prefixes_.end(); 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params->cancel) 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Canceled in the middle of a query, give up. 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We only need kMaxMatches results in the end, but before we get there we 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // need to promote lower-quality matches that are prefixes of 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // higher-quality matches, and remove lower-quality redirects. So we ask 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for more results than we need, of every prefix type, in hopes this will 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // give us far more than enough to work with. CullRedirects() will then 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // reduce the list to the best kMaxMatches results. 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch db->AutocompleteForPrefix(WideToUTF16(i->prefix + params->input.text()), 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kMaxMatches * 2, &url_matches); 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (URLRowVector::const_iterator j(url_matches.begin()); 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch j != url_matches.end(); ++j) { 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const Prefix* best_prefix = BestPrefix(j->url(), std::wstring()); 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(best_prefix != NULL); 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_matches.push_back(HistoryMatch(*j, i->prefix.length(), 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !i->num_components, 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i->num_components >= best_prefix->num_components)); 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create sorted list of suggestions. 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CullPoorMatches(&history_matches); 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SortMatches(&history_matches); 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PromoteOrCreateShorterSuggestion(db, *params, have_what_you_typed_match, 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch what_you_typed_match, &history_matches); 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to promote a match as an exact/inline autocomplete match. This also 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // moves it to the front of |history_matches|, so skip over it when 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // converting the rest of the matches. 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t first_match = 1; 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t exact_suggestion = 0; 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Checking |is_history_what_you_typed_match| tells us whether 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // SuggestExactInput() succeeded in constructing a valid match. 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (what_you_typed_match.is_history_what_you_typed_match && 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FixupExactSuggestion(db, params->input, &what_you_typed_match, 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &history_matches)) { 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Got an exact match for the user's input. Treat it as the best match 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // regardless of the input type. 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch exact_suggestion = 1; 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(what_you_typed_match); 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (params->input.prevent_inline_autocomplete() || 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_matches.empty() || 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !PromoteMatchForInlineAutocomplete(params, history_matches.front())) { 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Failed to promote any URLs for inline autocompletion. Use the What You 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Typed match, if we have it. 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch first_match = 0; 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (have_what_you_typed_match) 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(what_you_typed_match); 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This is the end of the synchronous pass. 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!backend) 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove redirects and trim list to size. We want to provide up to 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // kMaxMatches results plus the What You Typed result, if it was added to 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |history_matches| above. 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CullRedirects(backend, &history_matches, kMaxMatches + exact_suggestion); 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Convert the history matches to autocomplete matches. 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = first_match; i < history_matches.size(); ++i) { 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match = history_matches[i]; 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!have_what_you_typed_match || 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (match.url_info.url() != 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL(params->matches.front().destination_url))); 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(HistoryMatchToACMatch(params, match, NORMAL, 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_matches.size() - 1 - i)); 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called on the main thread when the query is complete. 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::QueryComplete( 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params_gets_deleted) { 234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Ensure |params_gets_deleted| gets deleted on exit. 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<HistoryURLProviderParams> params(params_gets_deleted); 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the user hasn't already started another query, clear our member pointer 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so we can't write into deleted memory. 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params_ == params_gets_deleted) 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_ = NULL; 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't send responses for queries that have been canceled. 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (params->cancel) 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Already set done_ when we canceled, no need to set it again. 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't modify |matches_| if the query failed, since it might have a default 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // match in it, whereas |params->matches| will be empty. 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!params->failed) { 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.swap(params->matches); 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateStarredStateOfMatches(); 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = true; 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch listener_->OnProviderUpdate(true); 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompleteMatch HistoryURLProvider::SuggestExactInput( 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool trim_http) { 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch match(this, 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CalculateRelevance(input.type(), WHAT_YOU_TYPED, 0), false, 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::URL_WHAT_YOU_TYPED); 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url = input.canonicalized_url(); 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (url.is_valid()) { 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.destination_url = url; 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Trim off "http://" if the user didn't type it. 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: We use TrimHttpPrefix() here rather than StringForURLDisplay() to 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // strip the scheme as we need to know the offset so we can adjust the 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |match_location| below. StringForURLDisplay() and TrimHttpPrefix() have 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // slightly different behavior as well (the latter will strip even without 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // two slashes after the scheme). 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring display_string(StringForURLDisplay(url, false, false)); 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t offset = trim_http ? TrimHttpPrefix(&display_string) : 0; 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.fill_into_edit = 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteInput::FormattedStringWithEquivalentMeaning(url, 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch display_string); 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: Don't set match.input_location (to allow inline autocompletion) 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // here, it's surprising and annoying. 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to highlight "innermost" match location. If we fix up "w" into 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "www.w.com", we want to highlight the fifth character, not the first. 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This relies on match.destination_url being the non-prefix-trimmed version 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // of match.contents. 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents = display_string; 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const Prefix* best_prefix = BestPrefix(match.destination_url, input.text()); 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Because of the vagaries of GURL, it's possible for match.destination_url 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to not contain the user's input at all. In this case don't mark anything 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as a match. 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t match_location = (best_prefix == NULL) ? 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring::npos : best_prefix->prefix.length() - offset; 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyLocationInString(match_location, 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input.text().length(), 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents.length(), 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::URL, 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.contents_class); 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.is_history_what_you_typed_match = true; 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return match; 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::FixupExactSuggestion(history::URLDatabase* db, 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch* match, 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches) const { 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(match != NULL); 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(matches != NULL); 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tricky corner case: The user has visited intranet site "foo", but not 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // internet site "www.foo.com". He types in foo (getting an exact match), 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // then tries to hit ctrl-enter. When pressing ctrl, the what-you-typed 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // match ("www.foo.com") doesn't show up in history, and thus doesn't get a 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // promoted relevance, but a different match from the input ("foo") does, and 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // gets promoted for inline autocomplete. Thus instead of getting 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "www.foo.com", the user still gets "foo" (and, before hitting enter, 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // probably gets an odd-looking inline autocomplete of "/"). 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We detect this crazy case as follows: 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * If the what-you-typed match is not in the history DB, 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * and the user has specified a TLD, 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * and the input _without_ the TLD _is_ in the history DB, 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * ...then just before pressing "ctrl" the best match we supplied was the 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // what-you-typed match, so stick with it by promoting this. 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLRow info; 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db->GetRowForURL(match->destination_url, &info)) { 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (input.desired_tld().empty()) 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL destination_url(URLFixerUpper::FixupURL(WideToUTF8(input.text()), 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string())); 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db->GetRowForURL(destination_url, &info)) 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We have data for this match, use it. 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match->deletable = true; 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match->description = UTF16ToWide(info.title()); 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyMatchInString(input.text(), 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UTF16ToWide(info.title()), 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::NONE, &match->description_class); 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote as an exact match. 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match->relevance = CalculateRelevance(input.type(), INLINE_AUTOCOMPLETE, 0); 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Put it on the front of the HistoryMatches for redirect culling. 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EnsureMatchPresent(info, std::wstring::npos, false, matches, true); 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::PromoteMatchForInlineAutocomplete( 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params, 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match) { 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote the first match if it's been typed at least n times, where n == 1 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for these long URLs because it's less likely that users will want to visit 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // them again. Even though we don't increment the typed_count for pasted-in 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // URLs, if the user manually edits the URL or types some long thing in by 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // hand, we wouldn't want to immediately start autocompleting it. 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!match.url_info.typed_count() || 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ((match.url_info.typed_count() == 1) && 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !IsHostOnly(match.url_info.url()))) 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->matches.push_back(HistoryMatchToACMatch(params, match, 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch INLINE_AUTOCOMPLETE, 0)); 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring HistoryURLProvider::FixupUserInput( 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input) { 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& input_text = input.text(); 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Fixup and canonicalize user input. 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL canonical_gurl(URLFixerUpper::FixupURL(WideToUTF8(input_text), 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string())); 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string canonical_gurl_str(canonical_gurl.possibly_invalid_spec()); 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (canonical_gurl_str.empty()) { 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This probably won't happen, but there are no guarantees. 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return input_text; 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the user types a number, GURL will convert it to a dotted quad. 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // However, if the parser did not mark this as a URL, then the user probably 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // didn't intend this interpretation. Since this can break history matching 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for hostname beginning with numbers (e.g. input of "17173" will be matched 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // against "0.0.67.21" instead of the original "17173", failing to find 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "17173.com"), swap the original hostname in for the fixed-up one. 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((input.type() != AutocompleteInput::URL) && 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch canonical_gurl.HostIsIPAddress()) { 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string original_hostname = 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch WideToUTF8(input_text.substr(input.parts().host.begin, 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input.parts().host.len)); 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const url_parse::Parsed& parts = 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch canonical_gurl.parsed_for_possibly_invalid_spec(); 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // parts.host must not be empty when HostIsIPAddress() is true. 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(parts.host.is_nonempty()); 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch canonical_gurl_str.replace(parts.host.begin, parts.host.len, 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch original_hostname); 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring output(UTF8ToWide(canonical_gurl_str)); 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't prepend a scheme when the user didn't have one. Since the fixer 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // upper only prepends the "http" scheme, that's all we need to check for. 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (canonical_gurl.SchemeIs(chrome::kHttpScheme) && 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !url_util::FindAndCompareScheme(WideToUTF8(input_text), 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch chrome::kHttpScheme, NULL)) 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch TrimHttpPrefix(&output); 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Make the number of trailing slashes on the output exactly match the input. 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Examples of why not doing this would matter: 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * The user types "a" and has this fixed up to "a/". Now no other sites 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // beginning with "a" will match. 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * The user types "file:" and has this fixed up to "file://". Now inline 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete will append too few slashes, resulting in e.g. "file:/b..." 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // instead of "file:///b..." 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // * The user types "http:/" and has this fixed up to "http:". Now inline 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete will append too many slashes, resulting in e.g. 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "http:///c..." instead of "http://c...". 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: We do this after calling TrimHttpPrefix() since that can strip 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // trailing slashes (if the scheme is the only thing in the input). It's not 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // clear that the result of fixup really matters in this case, but there's no 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // harm in making sure. 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t last_input_nonslash = input_text.find_last_not_of(L"/\\"); 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t num_input_slashes = (last_input_nonslash == std::wstring::npos) ? 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input_text.length() : (input_text.length() - 1 - last_input_nonslash); 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t last_output_nonslash = output.find_last_not_of(L"/\\"); 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t num_output_slashes = 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (last_output_nonslash == std::wstring::npos) ? 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output.length() : (output.length() - 1 - last_output_nonslash); 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (num_output_slashes < num_input_slashes) 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output.append(num_input_slashes - num_output_slashes, '/'); 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else if (num_output_slashes > num_input_slashes) 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output.erase(output.length() - num_output_slashes + num_input_slashes); 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return output; 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t HistoryURLProvider::TrimHttpPrefix(std::wstring* url) { 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Find any "http:". 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!HasHTTPScheme(*url)) 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 0; 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t scheme_pos = url->find(ASCIIToWide(chrome::kHttpScheme) + L":"); 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(scheme_pos != std::wstring::npos); 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Erase scheme plus up to two slashes. 448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t prefix_end = scheme_pos + strlen(chrome::kHttpScheme) + 1; 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const size_t after_slashes = std::min(url->length(), prefix_end + 2); 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while ((prefix_end < after_slashes) && ((*url)[prefix_end] == L'/')) 451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++prefix_end; 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch url->erase(scheme_pos, prefix_end - scheme_pos); 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return (scheme_pos == 0) ? prefix_end : 0; 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::IsHostOnly(const GURL& url) { 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(url.is_valid()); 459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return (!url.has_path() || (url.path() == "/")) && !url.has_query() && 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !url.has_ref(); 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HistoryURLProvider::CompareHistoryMatch(const HistoryMatch& a, 465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& b) { 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // A URL that has been typed at all is better than one that has never been 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // typed. (Note "!"s on each side) 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!a.url_info.typed_count() != !b.url_info.typed_count()) 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a.url_info.typed_count() > b.url_info.typed_count(); 470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Innermost matches (matches after any scheme or "www.") are better than 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // non-innermost matches. 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (a.innermost_match != b.innermost_match) 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a.innermost_match; 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // URLs that have been typed more often are better. 477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (a.url_info.typed_count() != b.url_info.typed_count()) 478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a.url_info.typed_count() > b.url_info.typed_count(); 479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // For URLs that have each been typed once, a host (alone) is better than a 481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // page inside. 482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (a.url_info.typed_count() == 1) { 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const bool a_is_host_only = IsHostOnly(a.url_info.url()); 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (a_is_host_only != IsHostOnly(b.url_info.url())) 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a_is_host_only; 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // URLs that have been visited more often are better. 489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (a.url_info.visit_count() != b.url_info.visit_count()) 490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a.url_info.visit_count() > b.url_info.visit_count(); 491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // URLs that have been visited more recently are better. 493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return a.url_info.last_visit() > b.url_info.last_visit(); 494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochHistoryURLProvider::Prefixes HistoryURLProvider::GetPrefixes() { 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We'll complete text following these prefixes. 499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: There's no requirement that these be in any particular order. 500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Prefixes prefixes; 501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"https://www.", 2)); 502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"http://www.", 2)); 503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"ftp://ftp.", 2)); 504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"ftp://www.", 2)); 505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"https://", 1)); 506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"http://", 1)); 507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"ftp://", 1)); 508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefixes.push_back(Prefix(L"", 0)); // Catches within-scheme matches as well 509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return prefixes; 510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint HistoryURLProvider::CalculateRelevance(AutocompleteInput::Type input_type, 514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MatchType match_type, 515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_number) { 516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch switch (match_type) { 517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case INLINE_AUTOCOMPLETE: 518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 1400; 519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case WHAT_YOU_TYPED: 521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 1200; 522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default: 524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return 900 + static_cast<int>(match_number); 525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 529c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGURL HistoryURLProvider::ConvertToHostOnly(const HistoryMatch& match, 530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& input) { 531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // See if we should try to do host-only suggestions for this URL. Nonstandard 532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // schemes means there's no authority section, so suggesting the host name 533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // is useless. File URLs are standard, but host suggestion is not useful for 534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // them either. 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url = match.url_info.url(); 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!url.is_valid() || !url.IsStandard() || url.SchemeIsFile()) 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GURL(); 538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Transform to a host-only match. Bail if the host no longer matches the 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // user input (e.g. because the user typed more than just a host). 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL host = url.GetWithEmptyPath(); 542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((host.spec().length() < (match.input_location + input.length()))) 543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GURL(); // User typing is longer than this host suggestion. 544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring spec = UTF8ToWide(host.spec()); 546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (spec.compare(match.input_location, input.length(), input)) 547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GURL(); // User typing is no longer a prefix. 548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return host; 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::PromoteOrCreateShorterSuggestion( 554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* db, 555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryURLProviderParams& params, 556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool have_what_you_typed_match, 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteMatch& what_you_typed_match, 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches) { 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (matches->empty()) 560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // No matches, nothing to do. 561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Determine the base URL from which to search, and whether that URL could 563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // itself be added as a match. We can add the base iff it's not "effectively 564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the same" as any "what you typed" match. 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& match = matches->front(); 566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL search_base = ConvertToHostOnly(match, params.input.text()); 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool can_add_search_base_to_matches = !have_what_you_typed_match; 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base.is_empty()) { 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Search from what the user typed when we couldn't reduce the best match 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to a host. Careful: use a substring of |match| here, rather than the 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // first match in |params|, because they might have different prefixes. If 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the user typed "google.com", |what_you_typed_match| will hold 573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "http://google.com/", but |match| might begin with 574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "http://www.google.com/". 575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO: this should be cleaned up, and is probably incorrect for IDN. 576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string new_match = match.url_info.url().possibly_invalid_spec(). 577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch substr(0, match.input_location + params.input.text().length()); 578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch search_base = GURL(new_match); 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base.is_empty()) 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Can't construct a valid URL from which to start a search. 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (!can_add_search_base_to_matches) { 582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch can_add_search_base_to_matches = 583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (search_base != what_you_typed_match.destination_url); 584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (search_base == match.url_info.url()) 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Couldn't shorten |match|, so no range of URLs to search over. 587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Search the DB for short URLs between our base and |match|. 589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLRow info(search_base); 590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool promote = true; 591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // A short URL is only worth suggesting if it's been visited at least a third 592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as often as the longer URL. 593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int min_visit_count = ((match.url_info.visit_count() - 1) / 3) + 1; 594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // For stability between the in-memory and on-disk autocomplete passes, when 595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the long URL has been typed before, only suggest shorter URLs that have 596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // also been typed. Otherwise, the on-disk pass could suggest a shorter URL 597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (which hasn't been typed) that the in-memory pass doesn't know about, 598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // thereby making the top match, and thus the behavior of inline 599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete, unstable. 600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int min_typed_count = match.url_info.typed_count() ? 1 : 0; 601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), 602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.url_info.url().possibly_invalid_spec(), min_visit_count, 603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch min_typed_count, can_add_search_base_to_matches, &info)) { 604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!can_add_search_base_to_matches) 605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Couldn't find anything and can't add the search base, bail. 606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Try to get info on the search base itself. Promote it to the top if the 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // original best match isn't good enough to autocomplete. 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch db->GetRowForURL(search_base, &info); 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch promote = match.url_info.typed_count() <= 1; 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Promote or add the desired URL to the list of matches. 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EnsureMatchPresent(info, match.input_location, match.match_in_scheme, 615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches, promote); 616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::EnsureMatchPresent( 620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const history::URLRow& info, 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring::size_type input_location, 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool match_in_scheme, 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool promote) { 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |matches| may already have an entry for this. 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (HistoryMatches::iterator i(matches->begin()); i != matches->end(); 627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i->url_info.url() == info.url()) { 629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Rotate it to the front if the caller wishes. 630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (promote) 631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::rotate(matches->begin(), i, i + 1); 632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // No entry, so create one. 637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatch match(info, input_location, match_in_scheme, true); 638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (promote) 639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->push_front(match); 640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->push_back(match); 642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::RunAutocompletePasses( 645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const AutocompleteInput& input, 646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool fixup_input_and_run_pass_1) { 647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.clear(); 648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((input.type() == AutocompleteInput::INVALID) || 650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (input.type() == AutocompleteInput::FORCED_QUERY)) 651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create a match for exactly what the user typed. This will only be used as 654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // a fallback in case we can't get the history service or URL DB; otherwise, 655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we'll run this again in DoAutocomplete() and use that result instead. 656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const bool trim_http = !HasHTTPScheme(input.text()); 657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't do this for queries -- while we can sometimes mark up a match for 658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this, it's not what the user wants, and just adds noise. 659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((input.type() != AutocompleteInput::QUERY) && 660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch input.canonicalized_url().is_valid()) 661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.push_back(SuggestExactInput(input, trim_http)); 662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We'll need the history service to run both passes, so try to obtain it. 664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryService* const history_service = 665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); 666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!history_service) 667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create the data structure for the autocomplete passes. We'll save this off 670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // onto the |params_| member for later deletion below if we need to run pass 671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. 672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring languages(languages_); 673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (languages.empty() && profile_) { 674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch languages = 675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UTF8ToWide(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); 676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<HistoryURLProviderParams> params( 678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new HistoryURLProviderParams(input, trim_http, languages)); 679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (fixup_input_and_run_pass_1) { 681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Do some fixup on the user input before matching against it, so we provide 682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // good results for local file paths, input with spaces, etc. 683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: This purposefully doesn't take input.desired_tld() into account; if 684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // it did, then holding "ctrl" would change all the results from the 685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HistoryURLProvider provider, not just the What You Typed Result. 686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring fixed_text(FixupUserInput(input)); 687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (fixed_text.empty()) { 688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Conceivably fixup could result in an empty string (although I don't 689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // have cases where this happens offhand). We can't do anything with 690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // empty input, so just bail; otherwise we'd crash later. 691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params->input.set_text(fixed_text); 694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 1: Get the in-memory URL database, and use it to find and promote 696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the inline autocomplete match, if any. 697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::URLDatabase* url_db = history_service->InMemoryDatabase(); 698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // url_db can be NULL if it hasn't finished initializing (or failed to 699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // initialize). In this case all we can do is fall back on the second 700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // pass. 701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(pkasting): We should just block here until this loads. Any time 703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // someone unloads the history backend, we'll get inconsistent inline 704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autocomplete behavior here. 705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (url_db) { 706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DoAutocomplete(NULL, url_db, params.get()); 707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // params->matches now has the matches we should expose to the provider. 708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 2 expects a "clean slate" set of matches. 709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.clear(); 710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches_.swap(params->matches); 711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateStarredStateOfMatches(); 712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Pass 2: Ask the history service to call us back on the history thread, 716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // where we can read the full on-disk DB. 717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!input.synchronous_only()) { 718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch done_ = false; 719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_ = params.release(); // This object will be destroyed in 720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // QueryComplete() once we're done with it. 721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_service->ScheduleAutocomplete(this, params_); 722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst HistoryURLProvider::Prefix* HistoryURLProvider::BestPrefix( 726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url, 727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& prefix_suffix) const { 728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const Prefix* best_prefix = NULL; 729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring text(UTF8ToWide(url.spec())); 730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (Prefixes::const_iterator i(prefixes_.begin()); i != prefixes_.end(); 731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((best_prefix == NULL) || 733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (i->num_components > best_prefix->num_components)) { 734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring prefix_with_suffix(i->prefix + prefix_suffix); 735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((text.length() >= prefix_with_suffix.length()) && 736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !text.compare(0, prefix_with_suffix.length(), prefix_with_suffix)) 737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch best_prefix = &(*i); 738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return best_prefix; 741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::SortMatches(HistoryMatches* matches) const { 744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Sort by quality, best first. 745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::sort(matches->begin(), matches->end(), &CompareHistoryMatch); 746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove duplicate matches (caused by the search string appearing in one of 748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the prefixes as well as after it). Consider the following scenario: 749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // User has visited "http://http.com" once and "http://htaccess.com" twice. 751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // User types "http". The autocomplete search with prefix "http://" returns 752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the first host, while the search with prefix "" returns both hosts. Now 753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we sort them into rank order: 754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://http.com (innermost_match) 755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://htaccess.com (!innermost_match, url_info.visit_count == 2) 756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://http.com (!innermost_match, url_info.visit_count == 1) 757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The above scenario tells us we can't use std::unique(), since our 759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // duplicates are not always sequential. It also tells us we should remove 760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the lower-quality duplicate(s), since otherwise the returned results won't 761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be ordered correctly. This is easy to do: we just always remove the later 762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // element of a duplicate pair. 763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Be careful! Because the vector contents may change as we remove elements, 764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we use an index instead of an iterator in the outer loop, and don't 765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // precalculate the ending position. 766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < matches->size(); ++i) { 767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator j(matches->begin() + i + 1); 768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (j != matches->end()) { 769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((*matches)[i].url_info.url() == j->url_info.url()) 770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch j = matches->erase(j); 771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++j; 773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::CullPoorMatches(HistoryMatches* matches) const { 778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const int kLowQualityMatchTypedLimit = 1; 779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const int kLowQualityMatchVisitLimit = 3; 780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const int kLowQualityMatchAgeLimitInDays = 3; 781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Time recent_threshold = 782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Time::Now() - TimeDelta::FromDays(kLowQualityMatchAgeLimitInDays); 783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (HistoryMatches::iterator i(matches->begin()); i != matches->end();) { 784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const history::URLRow& url_info = i->url_info; 785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((url_info.typed_count() <= kLowQualityMatchTypedLimit) && 786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (url_info.visit_count() <= kLowQualityMatchVisitLimit) && 787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (url_info.last_visit() < recent_threshold)) { 788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i = matches->erase(i); 789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i; 791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HistoryURLProvider::CullRedirects(history::HistoryBackend* backend, 796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t max_results) const { 798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t source = 0; 799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (source < matches->size()) && (source < max_results); ) { 800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url = (*matches)[source].url_info.url(); 801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(brettw) this should go away when everything uses GURL. 802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history::RedirectList redirects; 803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch backend->GetMostRecentRedirectsFrom(url, &redirects); 804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!redirects.empty()) { 805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove all but the first occurrence of any of these redirects in the 806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // search results. We also must add the URL we queried for, since it may 807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // not be the first match and we'd want to remove it. 808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // For example, when A redirects to B and our matches are [A, X, B], 810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we'll get B as the redirects from, and we want to remove the second 811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // item of that pair, removing B. If A redirects to B and our matches are 812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // [B, X, A], we'll want to remove A instead. 813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirects.push_back(url); 814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source = RemoveSubsequentMatchesOf(matches, source, redirects); 815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Advance to next item. 817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source++; 818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (matches->size() > max_results) 822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->resize(max_results); 823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t HistoryURLProvider::RemoveSubsequentMatchesOf( 826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches* matches, 827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t source_index, 828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::vector<GURL>& remove) const { 829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t next_index = source_index + 1; // return value = item after source 830c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 831c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Find the first occurrence of any URL in the redirect chain. We want to 832c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // keep this one since it is rated the highest. 833c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator first(std::find_first_of( 834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->begin(), matches->end(), remove.begin(), remove.end())); 835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(first != matches->end()) << 836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "We should have always found at least the original URL."; 837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Find any following occurrences of any URL in the redirect chain, these 839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // should be deleted. 840c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryMatches::iterator next(first); 841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next++; // Start searching immediately after the one we found already. 842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (next != matches->end() && 843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (next = std::find_first_of(next, matches->end(), remove.begin(), 844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch remove.end())) != matches->end()) { 845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Remove this item. When we remove an item before the source index, we 846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // need to shift it to the right and remember that so we can return it. 847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next = matches->erase(next); 848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (static_cast<size_t>(next - matches->begin()) < next_index) 849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch next_index--; 850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return next_index; 852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 854c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( 855c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HistoryURLProviderParams* params, 856c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const HistoryMatch& history_match, 857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MatchType match_type, 858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_number) { 859c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const history::URLRow& info = history_match.url_info; 860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch match(this, 861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CalculateRelevance(params->input.type(), match_type, match_number), 862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !!info.visit_count(), AutocompleteMatch::HISTORY_URL); 863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.destination_url = info.url(); 864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(match.destination_url.is_valid()); 865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t inline_autocomplete_offset = 866c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch history_match.input_location + params->input.text().length(); 867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & 868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ~((params->trim_http && !history_match.match_in_scheme) ? 869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 0 : net::kFormatUrlOmitHTTP); 870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.fill_into_edit = 871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), 872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::FormatUrl(info.url(), match_type == WHAT_YOU_TYPED ? 873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring() : params->languages, format_types, UnescapeRule::SPACES, 874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, NULL, &inline_autocomplete_offset)); 875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!params->input.prevent_inline_autocomplete()) 876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.inline_autocomplete_offset = inline_autocomplete_offset; 877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK((match.inline_autocomplete_offset == std::wstring::npos) || 878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (match.inline_autocomplete_offset <= match.fill_into_edit.length())); 879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t match_start = history_match.input_location; 881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents = net::FormatUrl(info.url(), 882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match_type == WHAT_YOU_TYPED ? std::wstring() : params->languages, 883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch format_types, UnescapeRule::SPACES, NULL, NULL, &match_start); 884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((match_start != std::wstring::npos) && 885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (inline_autocomplete_offset != std::wstring::npos) && 886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (inline_autocomplete_offset != match_start)) { 887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(inline_autocomplete_offset > match_start); 888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyLocationInString(match_start, 889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch inline_autocomplete_offset - match_start, match.contents.length(), 890c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::URL, &match.contents_class); 891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyLocationInString(std::wstring::npos, 0, 893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.contents.length(), ACMatchClassification::URL, 894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.contents_class); 895c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch match.description = UTF16ToWide(info.title()); 897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AutocompleteMatch::ClassifyMatchInString(params->input.text(), 898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UTF16ToWide(info.title()), 899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ACMatchClassification::NONE, 900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &match.description_class); 901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 902c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return match; 903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 904