| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autocomplete/autocomplete.h" | 5 #include "chrome/browser/autocomplete/autocomplete.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 #include <iterator> | |
| 9 | |
| 10 #include "base/string_util.h" | 7 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
| 12 #include "chrome/browser/autocomplete/autocomplete_controller_delegate.h" | |
| 13 #include "chrome/browser/autocomplete/autocomplete_match.h" | |
| 14 #include "chrome/browser/extensions/extension_service.h" | |
| 15 #include "chrome/browser/external_protocol/external_protocol_handler.h" | 9 #include "chrome/browser/external_protocol/external_protocol_handler.h" |
| 16 #include "chrome/browser/net/url_fixer_upper.h" | 10 #include "chrome/browser/net/url_fixer_upper.h" |
| 17 #include "chrome/browser/profiles/profile_io_data.h" | 11 #include "chrome/browser/profiles/profile_io_data.h" |
| 18 #include "chrome/common/url_constants.h" | 12 #include "content/public/common/url_constants.h" |
| 19 #include "googleurl/src/gurl.h" | |
| 20 #include "googleurl/src/url_canon_ip.h" | 13 #include "googleurl/src/url_canon_ip.h" |
| 21 #include "net/base/net_util.h" | 14 #include "net/base/net_util.h" |
| 22 #include "net/base/registry_controlled_domain.h" | 15 #include "net/base/registry_controlled_domain.h" |
| 23 | 16 |
| 24 | |
| 25 // AutocompleteInput ---------------------------------------------------------- | |
| 26 | |
| 27 AutocompleteInput::AutocompleteInput() | 17 AutocompleteInput::AutocompleteInput() |
| 28 : type_(INVALID), | 18 : type_(INVALID), |
| 29 prevent_inline_autocomplete_(false), | 19 prevent_inline_autocomplete_(false), |
| 30 prefer_keyword_(false), | 20 prefer_keyword_(false), |
| 31 allow_exact_keyword_match_(true), | 21 allow_exact_keyword_match_(true), |
| 32 matches_requested_(ALL_MATCHES) { | 22 matches_requested_(ALL_MATCHES) { |
| 33 } | 23 } |
| 34 | 24 |
| 35 AutocompleteInput::AutocompleteInput(const string16& text, | 25 AutocompleteInput::AutocompleteInput(const string16& text, |
| 36 const string16& desired_tld, | 26 const string16& desired_tld, |
| (...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 | 473 |
| 484 void AutocompleteInput::Clear() { | 474 void AutocompleteInput::Clear() { |
| 485 text_.clear(); | 475 text_.clear(); |
| 486 type_ = INVALID; | 476 type_ = INVALID; |
| 487 parts_ = url_parse::Parsed(); | 477 parts_ = url_parse::Parsed(); |
| 488 scheme_.clear(); | 478 scheme_.clear(); |
| 489 desired_tld_.clear(); | 479 desired_tld_.clear(); |
| 490 prevent_inline_autocomplete_ = false; | 480 prevent_inline_autocomplete_ = false; |
| 491 prefer_keyword_ = false; | 481 prefer_keyword_ = false; |
| 492 } | 482 } |
| 493 | |
| 494 // AutocompleteResult --------------------------------------------------------- | |
| 495 | |
| 496 // static | |
| 497 const size_t AutocompleteResult::kMaxMatches = 6; | |
| 498 const int AutocompleteResult::kLowestDefaultScore = 1200; | |
| 499 | |
| 500 void AutocompleteResult::Selection::Clear() { | |
| 501 destination_url = GURL(); | |
| 502 provider_affinity = NULL; | |
| 503 is_history_what_you_typed_match = false; | |
| 504 } | |
| 505 | |
| 506 AutocompleteResult::AutocompleteResult() { | |
| 507 // Reserve space for the max number of matches we'll show. | |
| 508 matches_.reserve(kMaxMatches); | |
| 509 | |
| 510 // It's probably safe to do this in the initializer list, but there's little | |
| 511 // penalty to doing it here and it ensures our object is fully constructed | |
| 512 // before calling member functions. | |
| 513 default_match_ = end(); | |
| 514 } | |
| 515 | |
| 516 AutocompleteResult::~AutocompleteResult() {} | |
| 517 | |
| 518 void AutocompleteResult::CopyFrom(const AutocompleteResult& rhs) { | |
| 519 if (this == &rhs) | |
| 520 return; | |
| 521 | |
| 522 matches_ = rhs.matches_; | |
| 523 // Careful! You can't just copy iterators from another container, you have to | |
| 524 // reconstruct them. | |
| 525 default_match_ = (rhs.default_match_ == rhs.end()) ? | |
| 526 end() : (begin() + (rhs.default_match_ - rhs.begin())); | |
| 527 | |
| 528 alternate_nav_url_ = rhs.alternate_nav_url_; | |
| 529 } | |
| 530 | |
| 531 void AutocompleteResult::CopyOldMatches(const AutocompleteInput& input, | |
| 532 const AutocompleteResult& old_matches) { | |
| 533 if (old_matches.empty()) | |
| 534 return; | |
| 535 | |
| 536 if (empty()) { | |
| 537 // If we've got no matches we can copy everything from the last result. | |
| 538 CopyFrom(old_matches); | |
| 539 for (ACMatches::iterator i = begin(); i != end(); ++i) | |
| 540 i->from_previous = true; | |
| 541 return; | |
| 542 } | |
| 543 | |
| 544 // In hopes of providing a stable popup we try to keep the number of matches | |
| 545 // per provider consistent. Other schemes (such as blindly copying the most | |
| 546 // relevant matches) typically result in many successive 'What You Typed' | |
| 547 // results filling all the matches, which looks awful. | |
| 548 // | |
| 549 // Instead of starting with the current matches and then adding old matches | |
| 550 // until we hit our overall limit, we copy enough old matches so that each | |
| 551 // provider has at least as many as before, and then use SortAndCull() to | |
| 552 // clamp globally. This way, old high-relevance matches will starve new | |
| 553 // low-relevance matches, under the assumption that the new matches will | |
| 554 // ultimately be similar. If the assumption holds, this prevents seeing the | |
| 555 // new low-relevance match appear and then quickly get pushed off the bottom; | |
| 556 // if it doesn't, then once the providers are done and we expire the old | |
| 557 // matches, the new ones will all become visible, so we won't have lost | |
| 558 // anything permanently. | |
| 559 ProviderToMatches matches_per_provider, old_matches_per_provider; | |
| 560 BuildProviderToMatches(&matches_per_provider); | |
| 561 old_matches.BuildProviderToMatches(&old_matches_per_provider); | |
| 562 for (ProviderToMatches::const_iterator i = old_matches_per_provider.begin(); | |
| 563 i != old_matches_per_provider.end(); ++i) { | |
| 564 MergeMatchesByProvider(i->second, matches_per_provider[i->first]); | |
| 565 } | |
| 566 | |
| 567 SortAndCull(input); | |
| 568 } | |
| 569 | |
| 570 void AutocompleteResult::AppendMatches(const ACMatches& matches) { | |
| 571 #ifndef NDEBUG | |
| 572 for (ACMatches::const_iterator i = matches.begin(); i != matches.end(); ++i) { | |
| 573 DCHECK_EQ(AutocompleteMatch::SanitizeString(i->contents), i->contents); | |
| 574 DCHECK_EQ(AutocompleteMatch::SanitizeString(i->description), | |
| 575 i->description); | |
| 576 } | |
| 577 #endif | |
| 578 std::copy(matches.begin(), matches.end(), std::back_inserter(matches_)); | |
| 579 default_match_ = end(); | |
| 580 alternate_nav_url_ = GURL(); | |
| 581 } | |
| 582 | |
| 583 void AutocompleteResult::AddMatch(const AutocompleteMatch& match) { | |
| 584 DCHECK(default_match_ != end()); | |
| 585 DCHECK_EQ(AutocompleteMatch::SanitizeString(match.contents), match.contents); | |
| 586 DCHECK_EQ(AutocompleteMatch::SanitizeString(match.description), | |
| 587 match.description); | |
| 588 ACMatches::iterator insertion_point = | |
| 589 std::upper_bound(begin(), end(), match, &AutocompleteMatch::MoreRelevant); | |
| 590 matches_difference_type default_offset = default_match_ - begin(); | |
| 591 if ((insertion_point - begin()) <= default_offset) | |
| 592 ++default_offset; | |
| 593 matches_.insert(insertion_point, match); | |
| 594 default_match_ = begin() + default_offset; | |
| 595 } | |
| 596 | |
| 597 void AutocompleteResult::SortAndCull(const AutocompleteInput& input) { | |
| 598 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) | |
| 599 i->ComputeStrippedDestinationURL(); | |
| 600 | |
| 601 // Remove duplicates. | |
| 602 std::sort(matches_.begin(), matches_.end(), | |
| 603 &AutocompleteMatch::DestinationSortFunc); | |
| 604 matches_.erase(std::unique(matches_.begin(), matches_.end(), | |
| 605 &AutocompleteMatch::DestinationsEqual), | |
| 606 matches_.end()); | |
| 607 | |
| 608 // Sort and trim to the most relevant kMaxMatches matches. | |
| 609 const size_t num_matches = std::min(kMaxMatches, matches_.size()); | |
| 610 std::partial_sort(matches_.begin(), matches_.begin() + num_matches, | |
| 611 matches_.end(), &AutocompleteMatch::MoreRelevant); | |
| 612 matches_.resize(num_matches); | |
| 613 | |
| 614 default_match_ = begin(); | |
| 615 | |
| 616 // Set the alternate nav URL. | |
| 617 alternate_nav_url_ = GURL(); | |
| 618 if (((input.type() == AutocompleteInput::UNKNOWN) || | |
| 619 (input.type() == AutocompleteInput::REQUESTED_URL)) && | |
| 620 (default_match_ != end()) && | |
| 621 (default_match_->transition != content::PAGE_TRANSITION_TYPED) && | |
| 622 (default_match_->transition != content::PAGE_TRANSITION_KEYWORD) && | |
| 623 (input.canonicalized_url() != default_match_->destination_url)) | |
| 624 alternate_nav_url_ = input.canonicalized_url(); | |
| 625 } | |
| 626 | |
| 627 bool AutocompleteResult::HasCopiedMatches() const { | |
| 628 for (ACMatches::const_iterator i = begin(); i != end(); ++i) { | |
| 629 if (i->from_previous) | |
| 630 return true; | |
| 631 } | |
| 632 return false; | |
| 633 } | |
| 634 | |
| 635 size_t AutocompleteResult::size() const { | |
| 636 return matches_.size(); | |
| 637 } | |
| 638 | |
| 639 bool AutocompleteResult::empty() const { | |
| 640 return matches_.empty(); | |
| 641 } | |
| 642 | |
| 643 AutocompleteResult::const_iterator AutocompleteResult::begin() const { | |
| 644 return matches_.begin(); | |
| 645 } | |
| 646 | |
| 647 AutocompleteResult::iterator AutocompleteResult::begin() { | |
| 648 return matches_.begin(); | |
| 649 } | |
| 650 | |
| 651 AutocompleteResult::const_iterator AutocompleteResult::end() const { | |
| 652 return matches_.end(); | |
| 653 } | |
| 654 | |
| 655 AutocompleteResult::iterator AutocompleteResult::end() { | |
| 656 return matches_.end(); | |
| 657 } | |
| 658 | |
| 659 // Returns the match at the given index. | |
| 660 const AutocompleteMatch& AutocompleteResult::match_at(size_t index) const { | |
| 661 DCHECK_LT(index, matches_.size()); | |
| 662 return matches_[index]; | |
| 663 } | |
| 664 | |
| 665 AutocompleteMatch* AutocompleteResult::match_at(size_t index) { | |
| 666 DCHECK_LT(index, matches_.size()); | |
| 667 return &matches_[index]; | |
| 668 } | |
| 669 | |
| 670 void AutocompleteResult::Reset() { | |
| 671 matches_.clear(); | |
| 672 default_match_ = end(); | |
| 673 } | |
| 674 | |
| 675 void AutocompleteResult::Swap(AutocompleteResult* other) { | |
| 676 const size_t default_match_offset = default_match_ - begin(); | |
| 677 const size_t other_default_match_offset = | |
| 678 other->default_match_ - other->begin(); | |
| 679 matches_.swap(other->matches_); | |
| 680 default_match_ = begin() + other_default_match_offset; | |
| 681 other->default_match_ = other->begin() + default_match_offset; | |
| 682 alternate_nav_url_.Swap(&(other->alternate_nav_url_)); | |
| 683 } | |
| 684 | |
| 685 #ifndef NDEBUG | |
| 686 void AutocompleteResult::Validate() const { | |
| 687 for (const_iterator i(begin()); i != end(); ++i) | |
| 688 i->Validate(); | |
| 689 } | |
| 690 #endif | |
| 691 | |
| 692 void AutocompleteResult::BuildProviderToMatches( | |
| 693 ProviderToMatches* provider_to_matches) const { | |
| 694 for (ACMatches::const_iterator i = begin(); i != end(); ++i) | |
| 695 (*provider_to_matches)[i->provider].push_back(*i); | |
| 696 } | |
| 697 | |
| 698 // static | |
| 699 bool AutocompleteResult::HasMatchByDestination(const AutocompleteMatch& match, | |
| 700 const ACMatches& matches) { | |
| 701 for (ACMatches::const_iterator i = matches.begin(); i != matches.end(); ++i) { | |
| 702 if (i->destination_url == match.destination_url) | |
| 703 return true; | |
| 704 } | |
| 705 return false; | |
| 706 } | |
| 707 | |
| 708 void AutocompleteResult::MergeMatchesByProvider(const ACMatches& old_matches, | |
| 709 const ACMatches& new_matches) { | |
| 710 if (new_matches.size() >= old_matches.size()) | |
| 711 return; | |
| 712 | |
| 713 size_t delta = old_matches.size() - new_matches.size(); | |
| 714 const int max_relevance = (new_matches.empty() ? | |
| 715 matches_.front().relevance : new_matches[0].relevance) - 1; | |
| 716 // Because the goal is a visibly-stable popup, rather than one that preserves | |
| 717 // the highest-relevance matches, we copy in the lowest-relevance matches | |
| 718 // first. This means that within each provider's "group" of matches, any | |
| 719 // synchronous matches (which tend to have the highest scores) will | |
| 720 // "overwrite" the initial matches from that provider's previous results, | |
| 721 // minimally disturbing the rest of the matches. | |
| 722 for (ACMatches::const_reverse_iterator i = old_matches.rbegin(); | |
| 723 i != old_matches.rend() && delta > 0; ++i) { | |
| 724 if (!HasMatchByDestination(*i, new_matches)) { | |
| 725 AutocompleteMatch match = *i; | |
| 726 match.relevance = std::min(max_relevance, match.relevance); | |
| 727 match.from_previous = true; | |
| 728 AddMatch(match); | |
| 729 delta--; | |
| 730 } | |
| 731 } | |
| 732 } | |
| 733 | |
| OLD | NEW |