Commit a04fff0b authored by mattreynolds's avatar mattreynolds Committed by Commit bot

Show PhysicalWebProvider suggestions with omnibox input

Add support for "Query Suggest" in the Physical Web omnibox provider.
Previously, the omnibox would only display Physical Web omnibox suggestions
when the user had not yet entered any text in the omnibox ("Zero Suggest").
With this CL, Physical Web suggestions may be displayed if the URL and page
title match all the terms in the omnibox query string.

BUG=630769

Review-Url: https://codereview.chromium.org/2591053002
Cr-Commit-Position: refs/heads/master@{#442098}
parent dad848c2
......@@ -77,6 +77,8 @@ static_library("browser") {
"omnibox_switches.h",
"omnibox_view.cc",
"omnibox_view.h",
"physical_web_node.cc",
"physical_web_node.h",
"physical_web_provider.cc",
"physical_web_provider.h",
"scored_history_match.cc",
......
......@@ -531,6 +531,47 @@ OmniboxFieldTrial::GetEmphasizeTitlesConditionForInput(
return static_cast<EmphasizeTitlesCondition>(value);
}
// static
bool OmniboxFieldTrial::InPhysicalWebZeroSuggestFieldTrial() {
return variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
kPhysicalWebZeroSuggestRule) ==
"true";
}
// static
bool OmniboxFieldTrial::InPhysicalWebAfterTypingFieldTrial() {
return variations::GetVariationParamValue(kBundledExperimentFieldTrialName,
kPhysicalWebAfterTypingRule) ==
"true";
}
// static
int OmniboxFieldTrial::GetPhysicalWebZeroSuggestBaseRelevance() {
std::string param_value(variations::GetVariationParamValue(
kBundledExperimentFieldTrialName,
kPhysicalWebZeroSuggestBaseRelevanceParam));
int base_relevance;
if (!param_value.empty() && base::StringToInt(param_value, &base_relevance))
return base_relevance;
// Default relevance score of the first Physical Web URL autocomplete match
// when the user has not typed in the omnibox. This score is intended to be
// between ClipboardURLProvider and ZeroSuggestProvider.
return 700;
}
// static
int OmniboxFieldTrial::GetPhysicalWebAfterTypingBaseRelevance() {
std::string param_value(variations::GetVariationParamValue(
kBundledExperimentFieldTrialName,
kPhysicalWebAfterTypingBaseRelevanceParam));
int base_relevance;
if (!param_value.empty() && base::StringToInt(param_value, &base_relevance))
return base_relevance;
// Default relevance score of the first Physical Web URL autocomplete match
// when the user is typing in the omnibox.
return 700;
}
const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
"OmniboxBundledExperimentV1";
const char OmniboxFieldTrial::kDisableProvidersRule[] = "DisableProviders";
......@@ -572,6 +613,10 @@ const char OmniboxFieldTrial::kKeywordRequiresPrefixMatchRule[] =
const char OmniboxFieldTrial::kKeywordScoreForSufficientlyCompleteMatchRule[] =
"KeywordScoreForSufficientlyCompleteMatch";
const char OmniboxFieldTrial::kEmphasizeTitlesRule[] = "EmphasizeTitles";
const char OmniboxFieldTrial::kPhysicalWebZeroSuggestRule[] =
"PhysicalWebZeroSuggest";
const char OmniboxFieldTrial::kPhysicalWebAfterTypingRule[] =
"PhysicalWebAfterTyping";
const char OmniboxFieldTrial::kHUPNewScoringEnabledParam[] =
"HUPExperimentalScoringEnabled";
......@@ -598,6 +643,11 @@ const char
OmniboxFieldTrial::kHQPExperimentalScoringTopicalityThresholdParam[] =
"HQPExperimentalScoringTopicalityThreshold";
const char OmniboxFieldTrial::kPhysicalWebZeroSuggestBaseRelevanceParam[] =
"PhysicalWebZeroSuggestBaseRelevance";
const char OmniboxFieldTrial::kPhysicalWebAfterTypingBaseRelevanceParam[] =
"PhysicalWebAfterTypingBaseRelevanceParam";
// static
int OmniboxFieldTrial::kDefaultMinimumTimeBetweenSuggestQueriesMs = 100;
......
......@@ -373,6 +373,26 @@ class OmniboxFieldTrial {
static EmphasizeTitlesCondition GetEmphasizeTitlesConditionForInput(
metrics::OmniboxInputType::Type input_type);
// ---------------------------------------------------------
// For PhysicalWebProvider related experiments.
// Returns whether the user is in a Physical Web field trial where the
// PhysicalWebProvider should be used to get suggestions when the user clicks
// on the omnibox but has not typed anything yet.
static bool InPhysicalWebZeroSuggestFieldTrial();
// Returns whether the user is in a Physical Web field trial and URL-based
// suggestions can continue to appear after the user has started typing.
static bool InPhysicalWebAfterTypingFieldTrial();
// Returns the base relevance score for Physical Web omnibox suggestions when
// the user has clicked on the omnibox but has not typed anything yet.
static int GetPhysicalWebZeroSuggestBaseRelevance();
// Returns the base relevance score for Physical Web omnibox suggestions when
// the user has started typing in the omnibox.
static int GetPhysicalWebAfterTypingBaseRelevance();
// ---------------------------------------------------------
// Exposed publicly for the sake of unittests.
static const char kBundledExperimentFieldTrialName[];
......@@ -405,6 +425,8 @@ class OmniboxFieldTrial {
static const char kKeywordScoreForSufficientlyCompleteMatchRule[];
static const char kHQPAllowDupMatchesForScoringRule[];
static const char kEmphasizeTitlesRule[];
static const char kPhysicalWebZeroSuggestRule[];
static const char kPhysicalWebAfterTypingRule[];
// Parameter names used by the HUP new scoring experiments.
static const char kHUPNewScoringEnabledParam[];
......@@ -421,6 +443,10 @@ class OmniboxFieldTrial {
static const char kHQPExperimentalScoringBucketsParam[];
static const char kHQPExperimentalScoringTopicalityThresholdParam[];
// Parameter names used by the Physical Web experimental scoring experiments.
static const char kPhysicalWebZeroSuggestBaseRelevanceParam[];
static const char kPhysicalWebAfterTypingBaseRelevanceParam[];
// The amount of time to wait before sending a new suggest request after the
// previous one unless overridden by a field trial parameter.
// Non-const because some unittests modify this value.
......
// Copyright 2016 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/physical_web_node.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/physical_web/data_source/physical_web_data_source.h"
PhysicalWebNode::PhysicalWebNode(const base::DictionaryValue& metadata_item) {
std::string title;
std::string url;
if (metadata_item.GetString(physical_web::kTitleKey, &title)) {
node_title_ = base::UTF8ToUTF16(title);
}
if (metadata_item.GetString(physical_web::kResolvedUrlKey, &url)) {
node_url_ = GURL(url);
}
}
PhysicalWebNode::~PhysicalWebNode() = default;
const base::string16& PhysicalWebNode::GetTitledUrlNodeTitle() const {
return node_title_;
}
const GURL& PhysicalWebNode::GetTitledUrlNodeUrl() const {
return node_url_;
}
// Copyright 2016 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_PHYSICAL_WEB_NODE_H_
#define COMPONENTS_OMNIBOX_BROWSER_PHYSICAL_WEB_NODE_H_
#include "components/bookmarks/browser/titled_url_node.h"
#include "url/gurl.h"
namespace base {
class DictionaryValue;
}
class PhysicalWebNode : public bookmarks::TitledUrlNode {
public:
explicit PhysicalWebNode(const base::DictionaryValue& metadata_item);
~PhysicalWebNode() override;
// TitledUrlNode
const base::string16& GetTitledUrlNodeTitle() const override;
const GURL& GetTitledUrlNodeUrl() const override;
private:
base::string16 node_title_;
GURL node_url_;
};
#endif // COMPONENTS_OMNIBOX_BROWSER_PHYSICAL_WEB_NODE_H_
......@@ -2,14 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/physical_web_provider.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/bookmarks/browser/titled_url_index.h"
#include "components/bookmarks/browser/titled_url_node_sorter.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/history_url_provider.h"
#include "components/omnibox/browser/physical_web_provider.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/physical_web_node.h"
#include "components/omnibox/browser/titled_url_match_utils.h"
#include "components/omnibox/browser/url_prefix.h"
#include "components/omnibox/browser/verbatim_match.h"
#include "components/physical_web/data_source/physical_web_data_source.h"
#include "components/url_formatter/url_formatter.h"
......@@ -20,17 +28,14 @@
namespace {
// Relevance score of the first Physical Web URL autocomplete match. This score
// is intended to be between ClipboardURLProvider and ZeroSuggestProvider.
// Subsequent matches should decrease in relevance to preserve the ordering
// in the metadata list.
static const int kPhysicalWebUrlBaseRelevance = 700;
// The maximum length of the page title's part of the overflow item's
// description. Longer titles will be truncated to this length. In a normal
// Physical Web match item (non-overflow item) we allow the omnibox display to
// truncate the title instead.
static const size_t kMaxTitleLengthInOverflow = 15;
// The maximum number of Physical Web URLs to retrieve from the index.
static const size_t kPhysicalWebIndexMaxMatches = 50;
}
// static
......@@ -52,11 +57,9 @@ void PhysicalWebProvider::Start(const AutocompleteInput& input,
done_ = false;
matches_.clear();
// Stop providing suggestions when the user enters text into the omnibox.
if (!input.from_omnibox_focus()) {
done_ = true;
return;
}
had_physical_web_suggestions_ = false;
if (input.from_omnibox_focus())
had_physical_web_suggestions_at_focus_or_later_ = false;
// Don't provide suggestions in incognito mode.
if (client_->IsOffTheRecord()) {
......@@ -73,16 +76,38 @@ void PhysicalWebProvider::Start(const AutocompleteInput& input,
return;
}
ConstructMatches(data_source->GetMetadata().get());
if (input.from_omnibox_focus()) {
ConstructZeroSuggestMatches(data_source->GetMetadata());
if (!matches_.empty()) {
had_physical_web_suggestions_ = true;
had_physical_web_suggestions_at_focus_or_later_ = true;
}
if (!zero_suggest_enabled_) {
matches_.clear();
}
// In zero-suggest, Physical Web matches should never be default. If the
// omnibox input is non-empty and we have at least one Physical Web match,
// add the current URL as the default so that hitting enter after focusing
// the omnibox causes the current page to reload. If the input field is
// empty, no default match is required.
if (!matches_.empty() && !input.text().empty()) {
matches_.push_back(VerbatimMatchForURL(
client_, input, input.current_url(), history_url_provider_, -1));
}
} else {
ConstructQuerySuggestMatches(data_source->GetMetadata(), input);
if (!matches_.empty()) {
had_physical_web_suggestions_ = true;
had_physical_web_suggestions_at_focus_or_later_ = true;
}
// Physical Web matches should never be default. If the omnibox input is
// non-empty and we have at least one Physical Web match, add the current URL
// as the default so that hitting enter after focusing the omnibox causes the
// current page to reload. If the input field is empty, no default match is
// required.
if (!matches_.empty() && !input.text().empty()) {
matches_.push_back(VerbatimMatchForURL(client_, input, input.current_url(),
history_url_provider_, -1));
if (!after_typing_enabled_) {
matches_.clear();
}
}
done_ = true;
......@@ -94,11 +119,23 @@ void PhysicalWebProvider::Stop(bool clear_cached_results,
}
void PhysicalWebProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
// AddProviderInfo is called for each autocomplete provider to allow
// provider-specific diagnostic info to be added to the omnibox log entry.
// In this case we do not append any diagnostic info and are taking advantage
// of the fact that this method is only called when the user has accepted an
// autocomplete suggestion.
// Record whether the provider could have provided a Physical Web suggestion,
// even if the suggestion could not be displayed due to the current field
// trial.
provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
new_entry.set_provider(AsOmniboxEventProviderType());
new_entry.set_provider_done(done_);
std::vector<uint32_t> field_trial_hashes;
OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes);
for (size_t i = 0; i < field_trial_hashes.size(); ++i) {
if (had_physical_web_suggestions_)
new_entry.mutable_field_trial_triggered()->Add(field_trial_hashes[i]);
if (had_physical_web_suggestions_at_focus_or_later_) {
new_entry.mutable_field_trial_triggered_in_session()->Add(
field_trial_hashes[i]);
}
}
// When the user accepts an autocomplete suggestion, record the number of
// nearby Physical Web URLs at the time the provider last constructed matches.
......@@ -111,12 +148,21 @@ PhysicalWebProvider::PhysicalWebProvider(
HistoryURLProvider* history_url_provider)
: AutocompleteProvider(AutocompleteProvider::TYPE_PHYSICAL_WEB),
client_(client),
history_url_provider_(history_url_provider) {}
history_url_provider_(history_url_provider),
zero_suggest_enabled_(
OmniboxFieldTrial::InPhysicalWebZeroSuggestFieldTrial()),
after_typing_enabled_(
OmniboxFieldTrial::InPhysicalWebAfterTypingFieldTrial()),
zero_suggest_base_relevance_(
OmniboxFieldTrial::GetPhysicalWebZeroSuggestBaseRelevance()),
after_typing_base_relevance_(
OmniboxFieldTrial::GetPhysicalWebAfterTypingBaseRelevance()) {}
PhysicalWebProvider::~PhysicalWebProvider() {
}
void PhysicalWebProvider::ConstructMatches(base::ListValue* metadata_list) {
void PhysicalWebProvider::ConstructZeroSuggestMatches(
std::unique_ptr<base::ListValue> metadata_list) {
nearby_url_count_ = metadata_list->GetSize();
size_t used_slots = 0;
......@@ -137,7 +183,7 @@ void PhysicalWebProvider::ConstructMatches(base::ListValue* metadata_list) {
// Add match items with decreasing relevance to preserve the ordering in
// the metadata list.
int relevance = kPhysicalWebUrlBaseRelevance - used_slots;
int relevance = zero_suggest_base_relevance_ - used_slots;
// Append an overflow item if creating a match for each metadata item would
// exceed the match limit.
......@@ -178,6 +224,41 @@ void PhysicalWebProvider::ConstructMatches(base::ListValue* metadata_list) {
"Omnibox.PhysicalWebProviderMatches", matches_.size(), kMaxMatches);
}
void PhysicalWebProvider::ConstructQuerySuggestMatches(
std::unique_ptr<base::ListValue> metadata_list,
const AutocompleteInput& input) {
// Passing nullptr for the TitledUrlNodeSorter will cause the returned match
// list to be unsorted.
bookmarks::TitledUrlIndex index(nullptr);
std::vector<std::unique_ptr<PhysicalWebNode>> nodes;
const size_t metadata_count = metadata_list->GetSize();
for (size_t i = 0; i < metadata_count; ++i) {
base::DictionaryValue* metadata_item = NULL;
if (metadata_list->GetDictionary(i, &metadata_item)) {
nodes.push_back(base::MakeUnique<PhysicalWebNode>(*metadata_item));
index.Add(nodes.back().get());
}
}
std::vector<bookmarks::TitledUrlMatch> titled_url_matches;
index.GetResultsMatching(input.text(), kPhysicalWebIndexMaxMatches,
query_parser::MatchingAlgorithm::DEFAULT,
&titled_url_matches);
size_t used_slots = 0;
const base::string16 fixed_up_input(FixupUserInput(input).second);
for (auto titled_url_match : titled_url_matches) {
const int relevance = after_typing_base_relevance_ - used_slots;
matches_.push_back(bookmarks::TitledUrlMatchToAutocompleteMatch(
titled_url_match, AutocompleteMatchType::PHYSICAL_WEB, relevance, this,
client_->GetSchemeClassifier(), input, fixed_up_input));
++used_slots;
if (matches_.size() >= kPhysicalWebMaxMatches) {
break;
}
}
}
void PhysicalWebProvider::AppendOverflowItem(int additional_url_count,
int relevance,
const base::string16& title) {
......
......@@ -6,6 +6,7 @@
#define COMPONENTS_OMNIBOX_BROWSER_PHYSICAL_WEB_PROVIDER_H_
#include "base/macros.h"
#include "components/bookmarks/browser/titled_url_match.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_provider.h"
......@@ -43,11 +44,21 @@ class PhysicalWebProvider : public AutocompleteProvider {
HistoryURLProvider* history_url_provider);
~PhysicalWebProvider() override;
// Adds a separate match item to |matches_| for each nearby URL in
// |metadata_list|, up to the maximum number of matches allowed. If the total
// number of nearby URLs exceeds this limit, one match is used for an overflow
// item.
void ConstructMatches(base::ListValue* metadata_list);
// When the user has focused the omnibox but not yet entered any text (i.e.,
// the Zero Suggest case), calling this method adds a separate match item to
// |matches_| for each nearby URL in |metadata_list|, up to the maximum number
// of matches allowed. If the total number of nearby URLs exceeds this limit,
// one match is used for an overflow item.
void ConstructZeroSuggestMatches(
std::unique_ptr<base::ListValue> metadata_list);
// When the user has entered text into the omnibox (i.e., the Query Suggest
// case), calling this method adds a separate match item to |matches_| for
// each nearby URL in |metadata_list| that matches all of the query terms in
// |input|, up to the maximum number of matches allowed.
void ConstructQuerySuggestMatches(
std::unique_ptr<base::ListValue> metadata_list,
const AutocompleteInput& input);
// Adds an overflow match item to |matches_| with a relevance score equal to
// |relevance| and a label indicating there are |additional_url_count| more
......@@ -68,6 +79,32 @@ class PhysicalWebProvider : public AutocompleteProvider {
// matches.
size_t nearby_url_count_;
// If true, provide suggestions when the user has focused the omnibox but has
// not typed anything.
bool zero_suggest_enabled_;
// If true, provide suggestions when the user has entered a query into the
// omnibox.
bool after_typing_enabled_;
// The base relevance score for Physical Web URL suggestions when the user has
// not typed anything into the omnibox.
int zero_suggest_base_relevance_;
// The base relevance score for Physical Web URL suggestions after the user
// has typed a query into the omnibox.
int after_typing_base_relevance_;
// Set to true if at least one suggestion was created the last time the
// provider was started, even if the suggestion could not be displayed due to
// a field trial.
bool had_physical_web_suggestions_;
// Set to true if at least one suggestion was created since the last time the
// omnibox was focused, even if the suggestion could not be displayed due to
// a field trial.
bool had_physical_web_suggestions_at_focus_or_later_;
DISALLOW_COPY_AND_ASSIGN(PhysicalWebProvider);
};
......
......@@ -8,15 +8,19 @@
#include <string>
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/metrics/proto/omnibox_event.pb.h"
#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "components/physical_web/data_source/fake_physical_web_data_source.h"
#include "components/physical_web/data_source/physical_web_data_source.h"
#include "components/physical_web/data_source/physical_web_listener.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/variations_associated_data.h"
#include "grit/components_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -72,10 +76,15 @@ class FakeAutocompleteProviderClient
class PhysicalWebProviderTest : public testing::Test {
protected:
PhysicalWebProviderTest() : provider_(NULL) {}
PhysicalWebProviderTest() : provider_(NULL) {
ResetFieldTrialList();
}
~PhysicalWebProviderTest() override {}
void SetUp() override {
base::FieldTrial* trial = CreatePhysicalWebFieldTrial();
trial->group();
client_.reset(new FakeAutocompleteProviderClient());
provider_ = PhysicalWebProvider::Create(client_.get(), nullptr);
}
......@@ -84,6 +93,25 @@ class PhysicalWebProviderTest : public testing::Test {
provider_ = NULL;
}
void ResetFieldTrialList() {
// Destroy the existing FieldTrialList before creating a new one to avoid a
// DCHECK.
field_trial_list_.reset();
field_trial_list_.reset(new base::FieldTrialList(
base::MakeUnique<metrics::SHA1EntropyProvider>("foo")));
variations::testing::ClearAllVariationParams();
}
static base::FieldTrial* CreatePhysicalWebFieldTrial() {
std::map<std::string, std::string> params;
params[OmniboxFieldTrial::kPhysicalWebZeroSuggestRule] = "true";
params[OmniboxFieldTrial::kPhysicalWebAfterTypingRule] = "true";
variations::AssociateVariationParams(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
return base::FieldTrialList::CreateFieldTrial(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
}
// Create a dummy metadata list with |metadata_count| items. Each item is
// populated with a unique scanned URL and page metadata.
static std::unique_ptr<base::ListValue> CreateMetadata(
......@@ -221,6 +249,7 @@ class PhysicalWebProviderTest : public testing::Test {
std::unique_ptr<FakeAutocompleteProviderClient> client_;
scoped_refptr<PhysicalWebProvider> provider_;
std::unique_ptr<base::FieldTrialList> field_trial_list_;
private:
DISALLOW_COPY_AND_ASSIGN(PhysicalWebProviderTest);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment