1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/zero_suggest_verbatim_match_provider.h"
#include <string>
#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/url_database.h"
#include "components/history/core/browser/url_row.h"
#include "components/omnibox/browser/autocomplete_enums.h"
#include "components/omnibox/browser/autocomplete_match_classification.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/suggestion_group_util.h"
#include "components/omnibox/browser/verbatim_match.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/search_engines/template_url_service.h"
#include "components/url_formatter/url_formatter.h"
namespace {
constexpr bool is_android = !!BUILDFLAG(IS_ANDROID);
// Returns whether specific context is eligible for a verbatim match.
// Only offer verbatim match on a site visit and SRP (no NTP etc).
bool IsVerbatimMatchEligible(
metrics::OmniboxEventProto::PageClassification context) {
using OEP = metrics::OmniboxEventProto;
// Only offer verbatim match on a site visit and SRP (no NTP etc).
return context == OEP::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT ||
context == OEP::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT ||
context == OEP::SEARCH_RESULT_PAGE_ON_CCT ||
context == OEP::ANDROID_SEARCH_WIDGET ||
context == OEP::ANDROID_SHORTCUTS_WIDGET ||
context == OEP::OTHER_ON_CCT || context == OEP::OTHER;
}
} // namespace
ZeroSuggestVerbatimMatchProvider::ZeroSuggestVerbatimMatchProvider(
AutocompleteProviderClient* client)
: AutocompleteProvider(TYPE_VERBATIM_MATCH), client_(client) {}
ZeroSuggestVerbatimMatchProvider::~ZeroSuggestVerbatimMatchProvider() = default;
void ZeroSuggestVerbatimMatchProvider::Start(const AutocompleteInput& input,
bool minimal_changes) {
Stop(AutocompleteStopReason::kClobbered);
if (!IsVerbatimMatchEligible(input.current_page_classification()))
return;
// Only offer verbatim match after the user just focused the Omnibox on NTP,
// SRP, or existing website view, or if the input field is empty.
if (!input.IsZeroSuggest()) {
return;
}
// For consistency with other zero-prefix providers.
const auto& page_url = input.current_url();
if (input.type() != metrics::OmniboxInputType::EMPTY &&
!(page_url.is_valid() &&
((page_url.scheme() == url::kHttpScheme) ||
(page_url.scheme() == url::kHttpsScheme) ||
(page_url.scheme() == url::kAboutScheme) ||
(page_url.scheme() ==
client_->GetEmbedderRepresentationOfAboutScheme())))) {
return;
}
CreateVerbatimMatch(input, input.current_title());
// It is possible for `title` to be empty if the page is currently loading.
// If title is empty and async matches are permitted, make an effort to
// retrieve page title from history database.
if (!matches_.back().description.empty() ||
input.omit_asynchronous_matches()) {
return;
}
history::HistoryService* const history_service = client_->GetHistoryService();
if (!history_service) {
return;
}
// Attempt to retrieve `title` from historical records.
done_ = false;
history_service->QueryURL(
input.current_url(), false,
base::BindOnce(&ZeroSuggestVerbatimMatchProvider::OnPageTitleRetrieved,
request_weak_ptr_factory_.GetWeakPtr(), input),
&task_tracker_);
}
void ZeroSuggestVerbatimMatchProvider::Stop(
AutocompleteStopReason stop_reason) {
AutocompleteProvider::Stop(stop_reason);
request_weak_ptr_factory_.InvalidateWeakPtrs();
}
void ZeroSuggestVerbatimMatchProvider::OnPageTitleRetrieved(
const AutocompleteInput& input,
history::QueryURLResult result) {
done_ = true;
// Re-create the item with a title collected from History service.
matches_.clear();
CreateVerbatimMatch(input, result.row.title());
}
void ZeroSuggestVerbatimMatchProvider::CreateVerbatimMatch(
const AutocompleteInput& input,
std::u16string page_title) {
AutocompleteInput verbatim_input = input;
verbatim_input.set_prevent_inline_autocomplete(true);
verbatim_input.set_allow_exact_keyword_match(false);
AutocompleteMatch match = VerbatimMatchForURL(
this, client_, verbatim_input, input.current_url(), std::move(page_title),
omnibox::kVerbatimMatchZeroSuggestRelevance);
// Make sure the URL is formatted the same was as most visited sites.
auto format_types = AutocompleteMatch::GetFormatTypes(false, false);
match.suggestion_group_id = omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX;
match.contents = url_formatter::FormatUrl(input.current_url(), format_types,
base::UnescapeRule::SPACES, nullptr,
nullptr, nullptr);
TermMatches term_matches;
if (input.text().length() > 0) {
term_matches = {{0, 0, input.text().length()}};
}
match.contents_class = ClassifyTermMatches(
term_matches, match.contents.size(),
ACMatchClassification::MATCH | ACMatchClassification::URL,
ACMatchClassification::URL);
// If the URL suggestion comes from the default search engine, extract the
// original search query and place it in fill_into_edit, to permit re-use of
// the query for manual refinement.
if constexpr (is_android) {
auto* const url_service = client_->GetTemplateURLService();
if (url_service->IsSearchResultsPageFromDefaultSearchProvider(
match.destination_url)) {
auto* const dse = url_service->GetDefaultSearchProvider();
if (dse->url_ref().SupportsReplacement(
url_service->search_terms_data())) {
dse->ExtractSearchTermsFromURL(match.destination_url,
url_service->search_terms_data(),
&match.contents);
// Upgrade Verbatim Match to a SEARCH_WHAY_YOU_TYPED.
match.type = AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED;
match.keyword = dse->keyword();
match.fill_into_edit = match.contents;
if (match.description.empty() ||
match.description ==
base::UTF8ToUTF16(match.destination_url.spec())) {
match.description = match.fill_into_edit;
if (match.description_class.empty()) {
match.description_class.push_back({0, ACMatchClassification::NONE});
}
}
}
}
}
match.provider = this;
matches_.push_back(std::move(match));
}
|