blob: a12c2bd7140b2190907d3b561b5870d3ec7bfecc [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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_pedal.h"
#include <cctype>
#include "base/strings/utf_string_conversions.h"
#include "components/omnibox/browser/omnibox_client.h"
#include "components/omnibox/browser/omnibox_edit_controller.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "ui/base/l10n/l10n_util.h"
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
#include "components/omnibox/browser/vector_icons.h" // nogncheck
#endif
OmniboxPedal::LabelStrings::LabelStrings(int id_hint,
int id_hint_short,
int id_suggestion_contents)
: hint(l10n_util::GetStringUTF16(id_hint)),
hint_short(l10n_util::GetStringUTF16(id_hint_short)),
suggestion_contents(l10n_util::GetStringUTF16(id_suggestion_contents)) {}
// =============================================================================
OmniboxPedal::SynonymGroup::SynonymGroup(
bool required,
std::initializer_list<const char*> synonyms)
: required_(required) {
// The DCHECK logic below is quickly ensuring that the synonyms are provided
// in descending order of string length.
#if DCHECK_IS_ON()
size_t min_size = std::numeric_limits<std::size_t>::max();
#endif
synonyms_.reserve(synonyms.size());
for (const char* synonym : synonyms) {
synonyms_.push_back(base::ASCIIToUTF16(synonym));
#if DCHECK_IS_ON()
size_t size = synonyms_.back().size();
DCHECK_LE(size, min_size);
min_size = size;
#endif
}
}
OmniboxPedal::SynonymGroup::SynonymGroup(const SynonymGroup& other) = default;
OmniboxPedal::SynonymGroup::~SynonymGroup() = default;
bool OmniboxPedal::SynonymGroup::EraseFirstMatchIn(
base::string16& remaining) const {
for (const auto& synonym : synonyms_) {
const size_t pos = remaining.find(synonym);
if (pos != base::string16::npos) {
remaining.erase(pos, synonym.size());
return true;
}
}
return !required_;
}
// =============================================================================
OmniboxPedal::OmniboxPedal(
LabelStrings strings,
GURL url,
std::initializer_list<const char*> triggers,
std::initializer_list<const SynonymGroup> synonym_groups)
: strings_(strings), url_(url) {
triggers_.reserve(triggers.size());
for (const char* trigger : triggers) {
triggers_.insert(base::ASCIIToUTF16(trigger));
}
synonym_groups_.reserve(synonym_groups.size());
for (const SynonymGroup& group : synonym_groups) {
synonym_groups_.push_back(group);
}
}
OmniboxPedal::~OmniboxPedal() {}
const OmniboxPedal::LabelStrings& OmniboxPedal::GetLabelStrings() const {
return strings_;
}
bool OmniboxPedal::IsNavigation() const {
return !url_.is_empty();
}
const GURL& OmniboxPedal::GetNavigationUrl() const {
return url_;
}
bool OmniboxPedal::ShouldExecute(bool button_pressed) const {
const auto mode = OmniboxFieldTrial::GetPedalSuggestionMode();
return (mode == OmniboxFieldTrial::PedalSuggestionMode::DEDICATED) ||
(mode == OmniboxFieldTrial::PedalSuggestionMode::IN_SUGGESTION &&
button_pressed);
}
bool OmniboxPedal::ShouldPresentButton() const {
return OmniboxFieldTrial::GetPedalSuggestionMode() ==
OmniboxFieldTrial::PedalSuggestionMode::IN_SUGGESTION;
}
void OmniboxPedal::Execute(OmniboxPedal::ExecutionContext& context) const {
DCHECK(IsNavigation());
OpenURL(context, url_);
}
bool OmniboxPedal::IsReadyToTrigger(
const AutocompleteProviderClient& client) const {
return true;
}
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
const gfx::VectorIcon& OmniboxPedal::GetVectorIcon() const {
return omnibox::kPedalIcon;
}
#endif
bool OmniboxPedal::IsTriggerMatch(const base::string16& match_text) const {
return (triggers_.find(match_text) != triggers_.end()) ||
IsConceptMatch(match_text);
}
bool OmniboxPedal::IsConceptMatch(const base::string16& match_text) const {
base::string16 remaining = match_text;
for (const auto& group : synonym_groups_) {
if (!group.EraseFirstMatchIn(remaining))
return false;
}
// If any non-space is remaining, it means there is something in match_text
// that was not covered by groups, so conservatively treat it as non-match.
const auto is_space = [](auto c) { return std::isspace(c); };
return std::all_of(remaining.begin(), remaining.end(), is_space);
}
void OmniboxPedal::OpenURL(OmniboxPedal::ExecutionContext& context,
const GURL& url) const {
context.controller_.OnAutocompleteAccept(
url, WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_GENERATED,
AutocompleteMatchType::PEDAL, context.match_selection_timestamp_);
}