history_provider.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/history_provider.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/utf_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_input.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_match.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/history.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/history_service_factory.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/net/url_fixer_upper.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "googleurl/src/url_util.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HistoryProvider::HistoryProvider(AutocompleteProviderListener* listener,
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 Profile* profile,
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 AutocompleteProvider::Type type)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : AutocompleteProvider(listener, profile, type),
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      always_prevent_inline_autocomplete_(false) {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HistoryProvider::DeleteMatch(const AutocompleteMatch& match) {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(done_);
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(profile_);
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(match.deletable);
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HistoryService* const history_service =
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete the match from the history DB.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(history_service);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(match.destination_url.is_valid());
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history_service->DeleteURL(match.destination_url);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeleteMatchFromMatches(match);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HistoryProvider::~HistoryProvider() {}
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HistoryProvider::DeleteMatchFromMatches(const AutocompleteMatch& match) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool found = false;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ACMatches::iterator i(matches_.begin()); i != matches_.end(); ++i) {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i->destination_url == match.destination_url && i->type == match.type) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      found = true;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (i->is_history_what_you_typed_match || i->starred) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // We can't get rid of What-You-Typed or Bookmarked matches,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // but we can make them look like they have no backing data.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i->deletable = false;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i->description.clear();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i->description_class.clear();
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        matches_.erase(i);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(found) << "Asked to delete a URL that isn't in our set of matches";
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  listener_->OnProviderUpdate(true);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HistoryProvider::FixupUserInput(AutocompleteInput* input) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16& input_text = input->text();
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Fixup and canonicalize user input.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: This purposefully doesn't take input.desired_tld() into account; if
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it did, then holding "ctrl" would change all the results from the provider,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // not just the What You Typed Result.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GURL canonical_gurl(URLFixerUpper::FixupURL(UTF16ToUTF8(input_text),
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    std::string()));
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string canonical_gurl_str(canonical_gurl.possibly_invalid_spec());
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (canonical_gurl_str.empty()) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This probably won't happen, but there are no guarantees.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the user types a number, GURL will convert it to a dotted quad.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // However, if the parser did not mark this as a URL, then the user probably
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // didn't intend this interpretation.  Since this can break history matching
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for hostname beginning with numbers (e.g. input of "17173" will be matched
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // against "0.0.67.21" instead of the original "17173", failing to find
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // "17173.com"), swap the original hostname in for the fixed-up one.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((input->type() != AutocompleteInput::URL) &&
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      canonical_gurl.HostIsIPAddress()) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string original_hostname =
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        UTF16ToUTF8(input_text.substr(input->parts().host.begin,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      input->parts().host.len));
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const url_parse::Parsed& parts =
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        canonical_gurl.parsed_for_possibly_invalid_spec();
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // parts.host must not be empty when HostIsIPAddress() is true.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(parts.host.is_nonempty());
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canonical_gurl_str.replace(parts.host.begin, parts.host.len,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               original_hostname);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 output = UTF8ToUTF16(canonical_gurl_str);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't prepend a scheme when the user didn't have one.  Since the fixer
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // upper only prepends the "http" scheme, that's all we need to check for.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (canonical_gurl.SchemeIs(chrome::kHttpScheme) &&
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !url_util::FindAndCompareScheme(UTF16ToUTF8(input_text),
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      chrome::kHttpScheme, NULL))
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TrimHttpPrefix(&output);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make the number of trailing slashes on the output exactly match the input.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Examples of why not doing this would matter:
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * The user types "a" and has this fixed up to "a/".  Now no other sites
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   beginning with "a" will match.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * The user types "file:" and has this fixed up to "file://".  Now inline
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   autocomplete will append too few slashes, resulting in e.g. "file:/b..."
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   instead of "file:///b..."
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * The user types "http:/" and has this fixed up to "http:".  Now inline
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   autocomplete will append too many slashes, resulting in e.g.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   "http:///c..." instead of "http://c...".
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: We do this after calling TrimHttpPrefix() since that can strip
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // trailing slashes (if the scheme is the only thing in the input).  It's not
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // clear that the result of fixup really matters in this case, but there's no
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // harm in making sure.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t last_input_nonslash =
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      input_text.find_last_not_of(ASCIIToUTF16("/\\"));
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t num_input_slashes = (last_input_nonslash == string16::npos) ?
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      input_text.length() : (input_text.length() - 1 - last_input_nonslash);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t last_output_nonslash =
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output.find_last_not_of(ASCIIToUTF16("/\\"));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t num_output_slashes =
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (last_output_nonslash == string16::npos) ?
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output.length() : (output.length() - 1 - last_output_nonslash);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_output_slashes < num_input_slashes)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output.append(num_input_slashes - num_output_slashes, '/');
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (num_output_slashes > num_input_slashes)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output.erase(output.length() - num_output_slashes + num_input_slashes);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_parse::Parsed parts;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLFixerUpper::SegmentURL(output, &parts);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  input->UpdateText(output, parts);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !output.empty();
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t HistoryProvider::TrimHttpPrefix(string16* url) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Find any "http:".
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!HasHTTPScheme(*url))
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t scheme_pos =
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url->find(ASCIIToUTF16(chrome::kHttpScheme) + char16(':'));
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(string16::npos, scheme_pos);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Erase scheme plus up to two slashes.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t prefix_end = scheme_pos + strlen(chrome::kHttpScheme) + 1;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t after_slashes = std::min(url->length(), prefix_end + 2);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ((prefix_end < after_slashes) && ((*url)[prefix_end] == '/'))
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++prefix_end;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url->erase(scheme_pos, prefix_end - scheme_pos);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (scheme_pos == 0) ? prefix_end : 0;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HistoryProvider::PreventInlineAutocomplete(
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AutocompleteInput& input) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return input.prevent_inline_autocomplete() ||
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      always_prevent_inline_autocomplete_ ||
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (!input.text().empty() &&
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       IsWhitespace(input.text()[input.text().length() - 1]));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
167