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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
// Copyright 2013 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/omnibox_controller.h"
#include <memory>
#include <string>
#include "base/functional/bind.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "components/omnibox/browser/autocomplete_classifier.h"
#include "components/omnibox/browser/autocomplete_controller_emitter.h"
#include "components/omnibox/browser/autocomplete_enums.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/omnibox_client.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/omnibox_popup_selection.h"
#include "components/omnibox/browser/omnibox_popup_view.h"
#include "components/omnibox/browser/page_classification_functions.h"
#include "components/omnibox/common/omnibox_feature_configs.h"
#include "components/search_engines/template_url_starter_pack_data.h"
#include "ui/gfx/geometry/rect.h"
OmniboxController::OmniboxController(
OmniboxView* view,
std::unique_ptr<OmniboxClient> client,
base::TimeDelta autocomplete_stop_timer_duration)
: client_(std::move(client)),
autocomplete_controller_(std::make_unique<AutocompleteController>(
client_->CreateAutocompleteProviderClient(),
AutocompleteClassifier::DefaultOmniboxProviders(),
autocomplete_stop_timer_duration)),
edit_model_(std::make_unique<OmniboxEditModel>(
/*omnibox_controller=*/this,
view)) {
// Directly observe omnibox's `AutocompleteController` instance - i.e., when
// `view` is provided in the constructor. In the case of realbox - i.e., when
// `view` is not provided in the constructor - `RealboxHandler` directly
// observes the `AutocompleteController` instance itself.
if (view) {
autocomplete_controller_->AddObserver(this);
}
// Register the `AutocompleteController` with `AutocompleteControllerEmitter`.
if (auto* emitter = client_->GetAutocompleteControllerEmitter()) {
autocomplete_controller_->AddObserver(emitter);
}
}
constexpr bool is_ios = !!BUILDFLAG(IS_IOS);
OmniboxController::~OmniboxController() = default;
void OmniboxController::StartAutocomplete(
const AutocompleteInput& input) const {
TRACE_EVENT0("omnibox", "OmniboxController::StartAutocomplete");
ClearPopupKeywordMode();
// We don't explicitly clear OmniboxPopupModel::manually_selected_match, as
// Start ends up invoking OmniboxPopupModel::OnResultChanged which clears it.
autocomplete_controller_->Start(input);
}
void OmniboxController::StopAutocomplete(bool clear_result) const {
TRACE_EVENT0("omnibox", "OmniboxController::StopAutocomplete");
autocomplete_controller_->Stop(clear_result
? AutocompleteStopReason::kClobbered
: AutocompleteStopReason::kInteraction);
}
void OmniboxController::StartZeroSuggestPrefetch() {
TRACE_EVENT0("omnibox", "OmniboxController::StartZeroSuggestPrefetch");
client_->MaybePrewarmForDefaultSearchEngine();
auto page_classification =
client_->GetPageClassification(/*is_prefetch=*/true);
GURL current_url = client_->GetURL();
std::u16string text = base::UTF8ToUTF16(current_url.spec());
if (omnibox::IsNTPPage(page_classification) || !is_ios) {
text.clear();
}
AutocompleteInput input(text, page_classification,
client_->GetSchemeClassifier());
input.set_current_url(current_url);
input.set_current_title(client_->GetTitle());
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_FOCUS);
autocomplete_controller_->StartPrefetch(input);
}
void OmniboxController::OnResultChanged(AutocompleteController* controller,
bool default_match_changed) {
TRACE_EVENT0("omnibox", "OmniboxController::OnResultChanged");
DCHECK(controller == autocomplete_controller_.get());
const bool popup_was_open = edit_model_->PopupIsOpen();
if (default_match_changed) {
// The default match has changed, we need to let the OmniboxEditModel know
// about new inline autocomplete text (blue highlight).
if (autocomplete_controller_->result().default_match()) {
edit_model_->OnCurrentMatchChanged();
} else {
edit_model_->OnPopupResultChanged();
edit_model_->OnPopupDataChanged(
std::u16string(),
/*is_temporary_text=*/false, std::u16string(), std::u16string(),
std::u16string(), false, std::u16string(), AutocompleteMatch());
}
} else {
edit_model_->OnPopupResultChanged();
}
const bool popup_is_open = edit_model_->PopupIsOpen();
if (popup_was_open != popup_is_open) {
client_->OnPopupVisibilityChanged(popup_is_open);
}
if (popup_was_open && !popup_is_open) {
// Accept the temporary text as the user text, because it makes little sense
// to have temporary text when the popup is closed.
edit_model_->AcceptTemporaryTextAsUserText();
// Closing the popup can change the default suggestion. This usually occurs
// when it's unclear whether the input represents a search or URL; e.g.,
// 'a.com/b c' or when title autocompleting. Clear the additional text to
// avoid suggesting the omnibox contains a URL suggestion when that may no
// longer be the case; i.e. when the default suggestion changed from a URL
// to a search suggestion upon closing the popup.
edit_model_->ClearAdditionalText();
}
// Note: The client outlives |this|, so bind a weak pointer to the callback
// passed in to eliminate the potential for crashes on shutdown.
// `should_preload` is set to `controller->done()` as prerender may only want
// to start preloading a result after all Autocomplete results are ready.
client_->OnResultChanged(
autocomplete_controller_->result(), default_match_changed,
/*should_preload=*/controller->done(),
base::BindRepeating(&OmniboxController::SetRichSuggestionBitmap,
weak_ptr_factory_.GetWeakPtr()));
}
void OmniboxController::ClearPopupKeywordMode() const {
TRACE_EVENT0("omnibox", "OmniboxController::ClearPopupKeywordMode");
if (edit_model_->PopupIsOpen()) {
OmniboxPopupSelection selection = edit_model_->GetPopupSelection();
if (selection.state == OmniboxPopupSelection::KEYWORD_MODE) {
selection.state = OmniboxPopupSelection::NORMAL;
edit_model_->SetPopupSelection(selection);
}
}
}
std::u16string OmniboxController::GetHeaderForSuggestionGroup(
omnibox::GroupId suggestion_group_id) const {
return autocomplete_controller_->result().GetHeaderForSuggestionGroup(
suggestion_group_id);
}
bool OmniboxController::IsSuggestionHidden(
const AutocompleteMatch& match) const {
if (OmniboxFieldTrial::IsStarterPackExpansionEnabled() &&
match.from_keyword) {
const TemplateURL* turl =
match.GetTemplateURL(client_->GetTemplateURLService(), false);
if (turl &&
turl->starter_pack_id() == template_url_starter_pack_data::kGemini) {
return true;
}
}
return false;
}
void OmniboxController::SetRichSuggestionBitmap(int result_index,
const GURL& icon_url,
const SkBitmap& bitmap) {
if (!icon_url.is_empty()) {
edit_model_->SetIconBitmap(icon_url, bitmap);
} else {
edit_model_->SetPopupRichSuggestionBitmap(result_index, bitmap);
}
}
|