blob: 9be53f56e7f93485eb2cff51637fd5a964a25d9e [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.
#ifndef COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_PEDAL_H_
#define COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_PEDAL_H_
#include <unordered_set>
#include "base/gtest_prod_util.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/omnibox/browser/buildflags.h"
#include "url/gurl.h"
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
namespace gfx {
struct VectorIcon;
}
#endif
class AutocompleteProviderClient;
class OmniboxEditController;
class OmniboxClient;
// Conceptually, a Pedal is a fixed action that can be taken by the user
// pressing a button or taking a new dedicated suggestion when some
// associated intention is detected in an omnibox match suggestion.
// The typical action is to navigate to an internal Chrome surface
// like settings, but other actions like translation or launching
// an incognito window are possible. The intention is detected by
// checking trigger queries against suggested match queries.
class OmniboxPedal {
public:
struct LabelStrings {
LabelStrings(int id_hint, int id_hint_short, int id_suggestion_contents);
const base::string16 hint;
const base::string16 hint_short;
const base::string16 suggestion_contents;
};
class SynonymGroup {
public:
// Note: synonyms must be specified in decreasing order by string length
// so that longest matches will be detected first. For example,
// "incognito window" must come before "incognito" so that the " window"
// part will also be covered by this group -- otherwise it would be left
// intact and wrongly treated as uncovered by the checking algorithm.
// See OmniboxPedal::IsConceptMatch for the logic that necessitates order.
SynonymGroup(bool required, std::initializer_list<const char*> synonyms);
SynonymGroup(const SynonymGroup& other);
~SynonymGroup();
// Removes first matching synonym from given |remaining| string if any are
// found. Returns true if checking may continue; false if no further
// checking is required because what remains cannot be a concept match.
bool EraseFirstMatchIn(base::string16& remaining) const;
protected:
// If this is true, a synonym of the group must be present for triggering.
// If false, then presence is simply allowed and does not inhibit triggering
// (any text not covered by groups would stop trigger).
bool required_;
// The set of interchangeable alternative representations for this group:
// when trying to clear browsing data, a user may think of 'erase', 'clear',
// 'delete', etc. Even though these are not strictly synonymous in natural
// language, they are considered equivalent within the context of intention
// to perform this Pedal's action.
std::vector<base::string16> synonyms_;
};
// ExecutionContext provides the necessary structure for Pedal
// execution implementations that potentially vary widely, and
// references are preferred over pointers for members that are
// not nullable. If there's ever a good case for changing to
// nullable pointers, this can change but for now presence is
// guaranteed so Pedals can use them without needing to check.
// The other reason to use a context is that it's easier to
// maintain with lots of Pedal implementations because the amount
// of boilerplate required is greatly reduced.
class ExecutionContext {
public:
ExecutionContext(OmniboxClient& client,
OmniboxEditController& controller,
base::TimeTicks match_selection_timestamp)
: client_(client),
controller_(controller),
match_selection_timestamp_(match_selection_timestamp) {}
OmniboxClient& client_;
OmniboxEditController& controller_;
base::TimeTicks match_selection_timestamp_;
};
OmniboxPedal(
LabelStrings strings,
GURL url,
std::initializer_list<const char*> triggers,
std::initializer_list<const OmniboxPedal::SynonymGroup> synonym_groups);
virtual ~OmniboxPedal();
// Provides read access to labels associated with this Pedal.
const LabelStrings& GetLabelStrings() const;
// Returns true if this is purely a navigation Pedal with URL.
bool IsNavigation() const;
// For navigation Pedals, returns the destination URL.
const GURL& GetNavigationUrl() const;
// These Should* methods can likely be eliminated when Pedal
// suggestion mode is firmly established.
// When a suggestion is selected by user, it may be via button press or
// normal click/keypress. This method tells whether the mode of selection
// taken should result in execution of the suggestion's Pedal.
virtual bool ShouldExecute(bool button_pressed) const;
// Some Pedals (or all, depending on mode) may be presented with a side
// button; this method returns true if this Pedal presents a button.
virtual bool ShouldPresentButton() const;
// Takes the action associated with this Pedal. Non-navigation
// Pedals must override the default, but Navigation Pedals don't need to.
virtual void Execute(ExecutionContext& context) const;
// Returns true if this Pedal is ready to be used now, or false if
// it does not apply under current conditions. (Example: the UpdateChrome
// Pedal may not be ready to trigger if no update is available.)
virtual bool IsReadyToTrigger(const AutocompleteProviderClient& client) const;
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
// Returns the vector icon to represent this Pedal's action in suggestion.
virtual const gfx::VectorIcon& GetVectorIcon() const;
#endif
// Returns true if the preprocessed match suggestion text triggers
// presentation of this Pedal. This is not intended for general use,
// and only OmniboxPedalProvider should need to call this method.
bool IsTriggerMatch(const base::string16& match_text) const;
protected:
FRIEND_TEST_ALL_PREFIXES(OmniboxPedalTest, SynonymGroupErasesFirstMatchOnly);
FRIEND_TEST_ALL_PREFIXES(OmniboxPedalTest, SynonymGroupsDriveConceptMatches);
FRIEND_TEST_ALL_PREFIXES(OmniboxPedalImplementationsTest,
UnorderedSynonymExpressionsAreConceptMatches);
// If a sufficient set of triggering synonym groups are present in match_text
// then it's a concept match and this returns true. If a required group is
// not present, or if match_text contains extraneous text not covered by any
// synonym group, then it's not a concept match and this returns false.
bool IsConceptMatch(const base::string16& match_text) const;
// Use this for the common case of navigating to a URL.
void OpenURL(ExecutionContext& context, const GURL& url) const;
std::vector<SynonymGroup> synonym_groups_;
std::unordered_set<base::string16> triggers_;
LabelStrings strings_;
// For navigation Pedals, this holds the destination URL; for action Pedals,
// this remains empty.
GURL url_;
};
#endif // COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_PEDAL_H_