blob: c0a412eefb65b2e29b72a85c5035994ba5c8aa77 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h"
#import <algorithm>
#import <iterator>
#import <memory>
#import <string>
#import <string_view>
#import <utility>
#import "base/auto_reset.h"
#import "base/feature_list.h"
#import "base/format_macros.h"
#import "base/functional/bind.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/histogram_macros.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/strcat.h"
#import "base/strings/string_number_conversions.h"
#import "base/strings/string_util.h"
#import "base/strings/utf_string_conversions.h"
#import "base/trace_event/trace_event.h"
#import "base/trace_event/typed_macros.h"
#import "build/branding_buildflags.h"
#import "build/build_config.h"
#import "components/bookmarks/browser/bookmark_model.h"
#import "components/dom_distiller/core/url_constants.h"
#import "components/dom_distiller/core/url_utils.h"
#import "components/history_embeddings/history_embeddings_features.h"
#import "components/navigation_metrics/navigation_metrics.h"
#import "components/omnibox/browser/actions/omnibox_action.h"
#import "components/omnibox/browser/actions/omnibox_pedal.h"
#import "components/omnibox/browser/actions/omnibox_pedal_concepts.h"
#import "components/omnibox/browser/autocomplete_classifier.h"
#import "components/omnibox/browser/autocomplete_match.h"
#import "components/omnibox/browser/autocomplete_match_type.h"
#import "components/omnibox/browser/autocomplete_provider.h"
#import "components/omnibox/browser/autocomplete_provider_client.h"
#import "components/omnibox/browser/history_fuzzy_provider.h"
#import "components/omnibox/browser/history_url_provider.h"
#import "components/omnibox/browser/keyword_provider.h"
#import "components/omnibox/browser/omnibox.mojom-shared.h"
#import "components/omnibox/browser/omnibox_client.h"
#import "components/omnibox/browser/omnibox_event_global_tracker.h"
#import "components/omnibox/browser/omnibox_field_trial.h"
#import "components/omnibox/browser/omnibox_log.h"
#import "components/omnibox/browser/omnibox_logging_utils.h"
#import "components/omnibox/browser/omnibox_metrics_provider.h"
#import "components/omnibox/browser/omnibox_navigation_observer.h"
#import "components/omnibox/browser/omnibox_popup_selection.h"
#import "components/omnibox/browser/omnibox_prefs.h"
#import "components/omnibox/browser/omnibox_text_util.h"
#import "components/omnibox/browser/page_classification_functions.h"
#import "components/omnibox/browser/search_provider.h"
#import "components/omnibox/browser/suggestion_answer.h"
#import "components/omnibox/browser/verbatim_match.h"
#import "components/omnibox/common/omnibox_feature_configs.h"
#import "components/omnibox/common/omnibox_features.h"
#import "components/prefs/pref_service.h"
#import "components/search_engines/search_engine_type.h"
#import "components/search_engines/template_url.h"
#import "components/search_engines/template_url_prepopulate_data.h"
#import "components/search_engines/template_url_service.h"
#import "components/search_engines/template_url_starter_pack_data.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h"
#import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h"
#import "ios/chrome/browser/omnibox/model/omnibox_text_controller.h"
#import "net/cookies/cookie_util.h"
#import "third_party/icu/source/common/unicode/ubidi.h"
#import "third_party/metrics_proto/omnibox_event.pb.h"
#import "third_party/metrics_proto/omnibox_focus_type.pb.h"
#import "url/third_party/mozilla/url_parse.h"
#import "url/url_util.h"
using bookmarks::BookmarkModel;
using metrics::OmniboxEventProto;
OmniboxEditModelIOS::OmniboxEditModelIOS(OmniboxControllerIOS* controller,
OmniboxTextModel* text_model)
: controller_(controller), text_model_(text_model) {}
OmniboxEditModelIOS::~OmniboxEditModelIOS() = default;
void OmniboxEditModelIOS::set_popup_view(OmniboxPopupViewIOS* popup_view) {
popup_view_ = popup_view;
}
void OmniboxEditModelIOS::set_text_controller(
OmniboxTextController* text_controller) {
text_controller_ = text_controller;
}
metrics::OmniboxEventProto::PageClassification
OmniboxEditModelIOS::GetPageClassification() const {
return controller_->client()->GetPageClassification(/*is_prefetch=*/false);
}
AutocompleteMatch OmniboxEditModelIOS::CurrentMatch(
GURL* alternate_nav_url) const {
// If we have a valid match use it. Otherwise get one for the current text.
AutocompleteMatch match = current_match_;
if (!match.destination_url.is_valid()) {
GetInfoForCurrentText(&match, alternate_nav_url);
} else if (alternate_nav_url) {
AutocompleteProviderClient* provider_client =
autocomplete_controller()->autocomplete_provider_client();
*alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
text_model_->input, match, provider_client);
}
return match;
}
bool OmniboxEditModelIOS::ResetDisplayTexts() {
const std::u16string old_display_text = GetPermanentDisplayText();
url_for_editing_ = controller_->client()->GetFormattedFullURL();
// When there's new permanent text, and the user isn't interacting with the
// omnibox, we want to revert the edit to show the new text. We could simply
// define "interacting" as "the omnibox has focus", but we still allow updates
// when the omnibox has focus as long as the user hasn't begun editing, and
// isn't seeing zerosuggestions (because changing this text would require
// changing or hiding those suggestions). When the omnibox doesn't have
// focus, we assume the user may have abandoned their interaction and it's
// always safe to change the text; this also prevents someone toggling "Show
// URL" (which sounds as if it might be persistent) from seeing just that URL
// forever afterwards.
return (GetPermanentDisplayText() != old_display_text) &&
(!has_focus() ||
(!text_model_->user_input_in_progress && !PopupIsOpen()));
}
std::u16string OmniboxEditModelIOS::GetPermanentDisplayText() const {
return url_for_editing_;
}
void OmniboxEditModelIOS::SetUserText(const std::u16string& text) {
[text_controller_ setInputInProgress:YES];
text_model_->UpdateUserText(text);
GetInfoForCurrentText(&current_match_, nullptr);
text_model_->paste_state = OmniboxPasteState::kNone;
}
void OmniboxEditModelIOS::OnChanged() {
// Don't call CurrentMatch() when there's no editing, as in this case we'll
// never actually use it. This avoids running the autocomplete providers (and
// any systems they then spin up) during startup.
const AutocompleteMatch& current_match = text_model_->user_input_in_progress
? CurrentMatch(nullptr)
: AutocompleteMatch();
controller_->client()->OnTextChanged(
current_match, text_model_->user_input_in_progress,
text_model_->user_text, autocomplete_controller()->result(), has_focus());
}
bool OmniboxEditModelIOS::CurrentTextIsURL() const {
// If !user_text_model_->inputin_progress_, we can determine if the text is a
// URL without starting the autocomplete system. This speeds browser startup.
return !text_model_->user_input_in_progress ||
!AutocompleteMatch::IsSearchType(CurrentMatch(nullptr).type);
}
void OmniboxEditModelIOS::AdjustTextForCopy(int sel_min,
std::u16string* text,
GURL* url_from_text,
bool* write_url) {
omnibox::AdjustTextForCopy(
sel_min, text,
/*has_user_modified_text=*/text_model_->user_input_in_progress ||
*text != url_for_editing_,
/*is_keyword_selected=*/false,
PopupIsOpen() ? std::optional<AutocompleteMatch>(CurrentMatch(nullptr))
: std::nullopt,
controller_->client(), url_from_text, write_url);
}
void OmniboxEditModelIOS::Revert() {
[text_controller_ setInputInProgress:NO];
text_model_->input.Clear();
text_model_->paste_state = OmniboxPasteState::kNone;
text_model_->UpdateUserText(std::u16string());
size_t start, end;
if (text_controller_) {
[text_controller_ getSelectionBounds:&start end:&end];
}
current_match_ = AutocompleteMatch();
// First home the cursor, so view of text is scrolled to left, then correct
// it. `SetCaretPos()` doesn't scroll the text, so doing that first wouldn't
// accomplish anything.
std::u16string current_permanent_url = GetPermanentDisplayText();
[text_controller_ setWindowText:current_permanent_url
caretPos:0
startAutocomplete:false
notifyTextChanged:true];
[text_controller_
setCaretPos:std::min(current_permanent_url.length(), start)];
controller_->client()->OnRevert();
}
void OmniboxEditModelIOS::OpenSelection(OmniboxPopupSelection selection,
base::TimeTicks timestamp,
WindowOpenDisposition disposition) {
// Intentionally accept input when selection has no line.
// This will usually reach `OpenMatch` indirectly.
if (selection.line >= autocomplete_controller()->result().size()) {
AcceptInput(disposition, timestamp);
return;
}
const AutocompleteMatch& match =
autocomplete_controller()->result().match_at(selection.line);
// Open the match.
GURL alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
text_model_->input, match,
autocomplete_controller()->autocomplete_provider_client());
OpenMatch(selection, match, disposition, alternate_nav_url, std::u16string(),
timestamp);
}
void OmniboxEditModelIOS::OpenSelection(base::TimeTicks timestamp,
WindowOpenDisposition disposition) {
OpenSelection(OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
OmniboxPopupSelection::NORMAL),
timestamp, disposition);
}
void OmniboxEditModelIOS::ClearAdditionalText() {
TRACE_EVENT0("omnibox", "OmniboxEditModelIOS::ClearAdditionalText");
[text_controller_ setAdditionalText:std::u16string()];
}
void OmniboxEditModelIOS::OnSetFocus() {
text_model_->OnSetFocus();
}
void OmniboxEditModelIOS::OnPaste() {
UMA_HISTOGRAM_COUNTS_1M("Omnibox.Paste", 1);
text_model_->paste_state = OmniboxPasteState::kPasting;
}
void OmniboxEditModelIOS::OpenMatchForTesting(
AutocompleteMatch match,
WindowOpenDisposition disposition,
const GURL& alternate_nav_url,
const std::u16string& pasted_text,
size_t index,
base::TimeTicks match_selection_timestamp) {
OpenMatch(OmniboxPopupSelection(index), match, disposition, alternate_nav_url,
pasted_text, match_selection_timestamp);
}
void OmniboxEditModelIOS::OnPopupDataChanged(
const std::u16string& inline_autocompletion,
const std::u16string& additional_text,
const AutocompleteMatch& new_match) {
current_match_ = new_match;
text_model_->inline_autocompletion = inline_autocompletion;
const std::u16string& user_text = text_model_->user_input_in_progress
? text_model_->user_text
: text_model_->input.text();
[text_controller_
updateAutocompleteIfTextChanged:user_text
autocompletion:text_model_->inline_autocompletion];
[text_controller_ setAdditionalText:additional_text];
// We need to invoke OnChanged in case the destination url changed (as could
// happen when control is toggled).
OnChanged();
}
bool OmniboxEditModelIOS::OnAfterPossibleChange(
const OmniboxStateChanges& state_changes) {
bool state_changed =
text_model_->UpdateStateAfterPossibleChange(state_changes);
if (!state_changed) {
return false;
}
[text_controller_ startAutocompleteAfterEdit];
return true;
}
// static
const char OmniboxEditModelIOS::kCutOrCopyAllTextHistogram[] =
"Omnibox.CutOrCopyAllText";
void OmniboxEditModelIOS::GetInfoForCurrentText(AutocompleteMatch* match,
GURL* alternate_nav_url) const {
DCHECK(match);
// If there's a query in progress or the popup is open, pick out the default
// match or selected match, if there is one.
bool found_match_for_text = false;
if (!autocomplete_controller()->done() || PopupIsOpen()) {
if (!autocomplete_controller()->done() &&
autocomplete_controller()->result().default_match()) {
// The user cannot have manually selected a match, or the query would have
// stopped. So the default match must be the desired selection.
*match = *autocomplete_controller()->result().default_match();
found_match_for_text = true;
}
if (found_match_for_text && alternate_nav_url) {
AutocompleteProviderClient* provider_client =
autocomplete_controller()->autocomplete_provider_client();
*alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
text_model_->input, *match, provider_client);
}
}
if (!found_match_for_text) {
// For match generation, we use the unelided `url_for_editing_`, unless the
// user input is in progress.
std::u16string text_for_match_generation =
text_model_->user_input_in_progress ? text_model_->user_text
: url_for_editing_;
controller_->client()->GetAutocompleteClassifier()->Classify(
text_for_match_generation, false, true, GetPageClassification(), match,
alternate_nav_url);
}
}
bool OmniboxEditModelIOS::PopupIsOpen() const {
return popup_view_ && popup_view_->IsOpen();
}
void OmniboxEditModelIOS::SetAutocompleteInput(AutocompleteInput input) {
text_model_->input = std::move(input);
}
PrefService* OmniboxEditModelIOS::GetPrefService() {
return controller_->client()->GetPrefs();
}
const PrefService* OmniboxEditModelIOS::GetPrefService() const {
return controller_->client()->GetPrefs();
}
AutocompleteController* OmniboxEditModelIOS::autocomplete_controller() const {
return controller_->autocomplete_controller();
}
void OmniboxEditModelIOS::AcceptInput(
WindowOpenDisposition disposition,
base::TimeTicks match_selection_timestamp) {
// Get the URL and transition type for the selected entry.
GURL alternate_nav_url;
AutocompleteMatch match = CurrentMatch(&alternate_nav_url);
if (!match.destination_url.is_valid()) {
return;
}
if (text_model_->paste_state != OmniboxPasteState::kNone &&
match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED) {
// When the user pasted in a URL and hit enter, score it like a link click
// rather than a normal typed URL, so it doesn't get inline autocompleted
// as aggressively later.
match.transition = ui::PAGE_TRANSITION_LINK;
}
if (popup_view_) {
OpenMatch(OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch), match,
disposition, alternate_nav_url, std::u16string(),
match_selection_timestamp);
}
}
void OmniboxEditModelIOS::OpenMatch(OmniboxPopupSelection selection,
AutocompleteMatch match,
WindowOpenDisposition disposition,
const GURL& alternate_nav_url,
const std::u16string& pasted_text,
base::TimeTicks match_selection_timestamp) {
// If the user is executing an action, this will be non-null and some match
// opening and metrics behavior will be adjusted accordingly.
OmniboxAction* action = nullptr;
if (selection.state == OmniboxPopupSelection::NORMAL &&
match.takeover_action) {
DCHECK(match_selection_timestamp != base::TimeTicks());
action = match.takeover_action.get();
} else if (selection.IsAction()) {
DCHECK_LT(selection.action_index, match.actions.size());
action = match.actions[selection.action_index].get();
}
// Invalid URLs such as chrome://history can end up here, but that's okay
// if the user is executing an action instead of navigating to the URL.
if (!match.destination_url.is_valid() && !action) {
return;
}
// NULL_RESULT_MESSAGE matches are informational only and cannot be acted
// upon. Immediately return when attempting to open one.
if (match.type == AutocompleteMatchType::NULL_RESULT_MESSAGE) {
return;
}
// Also switch the window disposition for tab switch actions. The action
// itself will already open with SWITCH_TO_TAB disposition, but the change
// is needed earlier for metrics.
bool is_tab_switch_action =
action && action->ActionId() == OmniboxActionId::TAB_SWITCH;
if (is_tab_switch_action) {
disposition = WindowOpenDisposition::SWITCH_TO_TAB;
}
TRACE_EVENT("omnibox", "OmniboxEditModelIOS::OpenMatch", "match", match,
"disposition", disposition, "altenate_nav_url", alternate_nav_url,
"pasted_text", pasted_text);
const base::TimeTicks& now(base::TimeTicks::Now());
base::TimeDelta elapsed_time_since_user_first_modified_omnibox(
now - text_model_->time_user_first_modified_omnibox);
autocomplete_controller()
->UpdateMatchDestinationURLWithAdditionalSearchboxStats(
elapsed_time_since_user_first_modified_omnibox, &match);
GURL destination_url = action ? action->getUrl() : match.destination_url;
// Save the result of the interaction, but do not record the histogram yet.
text_model_->focus_resulted_in_navigation = true;
omnibox::RecordActionShownForAllActions(autocomplete_controller()->result(),
selection);
HistoryFuzzyProvider::RecordOpenMatchMetrics(
autocomplete_controller()->result(), match);
std::u16string input_text(pasted_text);
if (input_text.empty()) {
input_text = text_model_->user_input_in_progress ? text_model_->user_text
: url_for_editing_;
}
// Create a dummy AutocompleteInput for use in calling VerbatimMatchForInput()
// to create an alternate navigational match.
AutocompleteInput alternate_input(
input_text, GetPageClassification(),
controller_->client()->GetSchemeClassifier(),
controller_->client()->ShouldDefaultTypedNavigationsToHttps(), 0, false);
// Somehow we can occasionally get here with no active tab. It's not
// clear why this happens.
alternate_input.set_current_url(controller_->client()->GetURL());
alternate_input.set_current_title(controller_->client()->GetTitle());
base::TimeDelta elapsed_time_since_last_change_to_default_match(
now - autocomplete_controller()->last_time_default_match_changed());
DCHECK(match.provider);
// These elapsed times don't really make sense for matches that come from
// omnibox focus (because the user did not modify the omnibox), so for those
// we set the elapsed times to something that will be ignored by
// metrics_log.cc. They also don't necessarily make sense if the omnibox
// dropdown is closed or the user used paste-and-go. (In most
// cases when this happens, the user never modified the omnibox.)
const bool popup_open = PopupIsOpen();
const base::TimeDelta default_time_delta = base::Milliseconds(-1);
if (text_model_->input.IsZeroSuggest() || !pasted_text.empty()) {
elapsed_time_since_user_first_modified_omnibox = default_time_delta;
elapsed_time_since_last_change_to_default_match = default_time_delta;
}
base::TimeDelta elapsed_time_since_user_focused_omnibox = default_time_delta;
if (!text_model_->last_omnibox_focus.is_null()) {
elapsed_time_since_user_focused_omnibox =
now - text_model_->last_omnibox_focus;
// Only record focus to open time when a focus actually happened (as
// opposed to, say, dragging a link onto the omnibox).
omnibox::LogFocusToOpenTime(
elapsed_time_since_user_focused_omnibox,
text_model_->input.IsZeroSuggest(), GetPageClassification(), match,
selection.IsAction() ? selection.action_index : -1);
}
// In some unusual cases, we ignore autocomplete_controller()->result() and
// instead log a fake result set with a single element (`match`) and
// selected_index of 0. For these cases:
// 1. If the popup is closed (there is no result set). This doesn't apply
// for WebUI searchboxes since they don't have an associated popup.
// 2. If the index is out of bounds. This should only happen if
// `selection.line` is
// kNoMatch, which can happen if the default search provider is disabled.
// 3. If this is paste-and-go (meaning the contents of the dropdown
// are ignored regardless).
const bool dropdown_ignored =
(!popup_open && !omnibox::IsWebUISearchbox(GetPageClassification())) ||
selection.line >= autocomplete_controller()->result().size() ||
!pasted_text.empty();
ACMatches fake_single_entry_matches;
fake_single_entry_matches.push_back(match);
AutocompleteResult fake_single_entry_result;
fake_single_entry_result.AppendMatches(fake_single_entry_matches);
std::u16string user_text =
text_model_->input.IsZeroSuggest() ? std::u16string() : input_text;
size_t completed_length = match.allowed_to_be_default_match
? match.inline_autocompletion.length()
: std::u16string::npos;
bool is_incognito = autocomplete_controller()
->autocomplete_provider_client()
->IsOffTheRecord();
OmniboxLog log(
user_text, text_model_->just_deleted_text, text_model_->input.type(),
/*is_keyword_selected=*/false, OmniboxEventProto::INVALID, popup_open,
dropdown_ignored ? OmniboxPopupSelection(0) : selection, disposition,
!pasted_text.empty(),
SessionID::InvalidValue(), // don't know tab ID; set later if appropriate
GetPageClassification(), elapsed_time_since_user_first_modified_omnibox,
completed_length, elapsed_time_since_last_change_to_default_match,
dropdown_ignored ? fake_single_entry_result
: autocomplete_controller()->result(),
destination_url, is_incognito, text_model_->input.IsZeroSuggest(),
match.session);
// Check disabled on iOS as the platform shows a default suggestion on focus
// (crbug.com/40061502).
#if !BUILDFLAG(IS_IOS)
DCHECK(dropdown_ignored ||
(log.elapsed_time_since_user_first_modified_omnibox >=
log.elapsed_time_since_last_change_to_default_match))
<< "We should've got the notification that the user modified the "
<< "omnibox text at same time or before the most recent time the "
<< "default match changed.";
#endif
log.elapsed_time_since_user_focused_omnibox =
elapsed_time_since_user_focused_omnibox;
log.ukm_source_id = controller_->client()->GetUKMSourceId();
if ((disposition == WindowOpenDisposition::CURRENT_TAB) &&
controller_->client()->CurrentPageExists()) {
// If we know the destination is being opened in the current tab,
// we can easily get the tab ID. (If it's being opened in a new
// tab, we don't know the tab ID yet.)
log.tab_id = controller_->client()->GetSessionID();
}
autocomplete_controller()->AddProviderAndTriggeringLogs(&log);
base::UmaHistogramEnumeration("Omnibox.SuggestionUsed.RichAutocompletion",
match.rich_autocompletion_triggered);
omnibox::LogIPv4PartsCount(user_text, destination_url, completed_length);
controller_->client()->OnURLOpenedFromOmnibox(&log);
OmniboxEventGlobalTracker::GetInstance()->OnURLOpened(&log);
LOCAL_HISTOGRAM_BOOLEAN("Omnibox.EventCount", true);
omnibox::answer_data_parser::LogAnswerUsed(match.answer_type);
TemplateURLService* service = controller_->client()->GetTemplateURLService();
TemplateURL* template_url = match.GetTemplateURL(service, false);
if (template_url) {
// `match` is a Search navigation; log search engine usage metrics.
AutocompleteMatch::LogSearchEngineUsed(match, service);
DCHECK(ui::PageTransitionTypeIncludingQualifiersIs(
match.transition, ui::PAGE_TRANSITION_GENERATED) ||
ui::PageTransitionTypeIncludingQualifiersIs(
match.transition, ui::PAGE_TRANSITION_RELOAD));
} else {
// `match` is a URL navigation, not a search.
// For logging the below histogram, only record uses that depend on the
// omnibox suggestion system, i.e., TYPED navigations. That is, exclude
// omnibox URL interactions that are treated as reloads or link-following
// (i.e., cut-and-paste of URLs) or paste-and-go.
if (ui::PageTransitionTypeIncludingQualifiersIs(
match.transition, ui::PAGE_TRANSITION_TYPED) &&
pasted_text.empty()) {
navigation_metrics::RecordOmniboxURLNavigation(destination_url);
}
// The following histograms should be recorded for both TYPED and pasted
// URLs, but should still exclude reloads.
if (ui::PageTransitionTypeIncludingQualifiersIs(
match.transition, ui::PAGE_TRANSITION_TYPED) ||
ui::PageTransitionTypeIncludingQualifiersIs(match.transition,
ui::PAGE_TRANSITION_LINK)) {
net::cookie_util::RecordCookiePortOmniboxHistograms(destination_url);
}
}
if (action) {
OmniboxAction::ExecutionContext context(
*(autocomplete_controller()->autocomplete_provider_client()),
base::BindOnce(&OmniboxClient::OnAutocompleteAccept,
controller_->client()->AsWeakPtr()),
match_selection_timestamp, disposition);
action->Execute(context);
}
if (disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB) {
base::AutoReset<bool> tmp(&text_model_->in_revert, true);
[text_controller_ revertAll]; // Revert the box to its unedited state.
}
if (!action) {
// Track whether the destination URL sends us to a search results page
// using the default search provider.
TemplateURLService* template_url_service =
controller_->client()->GetTemplateURLService();
if (template_url_service &&
template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
match.destination_url)) {
base::RecordAction(
base::UserMetricsAction("OmniboxDestinationURLIsSearchOnDSP"));
base::UmaHistogramBoolean("Omnibox.Search.OffTheRecord", is_incognito);
}
BookmarkModel* bookmark_model = controller_->client()->GetBookmarkModel();
if (bookmark_model && bookmark_model->IsBookmarked(destination_url)) {
controller_->client()->OnBookmarkLaunched();
}
// This block should be the last call in OpenMatch, because controller_ is
// not guaranteed to exist after client()->OnAutocompleteAccept is called.
if (destination_url.is_valid()) {
// This calls RevertAll again.
base::AutoReset<bool> tmp(&text_model_->in_revert, true);
controller_->client()->OnAutocompleteAccept(
destination_url, match.post_content.get(), disposition,
ui::PageTransitionFromInt(match.transition |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
match.type, match_selection_timestamp,
text_model_->input.added_default_scheme_to_typed_url(),
text_model_->input.typed_url_had_http_scheme() &&
match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED,
input_text, match,
VerbatimMatchForInput(
autocomplete_controller()->history_url_provider(),
autocomplete_controller()->autocomplete_provider_client(),
alternate_input, alternate_nav_url, false));
}
}
}
std::u16string OmniboxEditModelIOS::GetText() const {
return [text_controller_ displayedText];
}