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 |