| OLD | NEW |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/omnibox/browser/physical_web_provider.h" |
| 6 |
| 5 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 6 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/strings/string_util.h" |
| 7 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 8 #include "base/values.h" | 11 #include "base/values.h" |
| 12 #include "components/bookmarks/browser/titled_url_index.h" |
| 13 #include "components/bookmarks/browser/titled_url_node_sorter.h" |
| 9 #include "components/omnibox/browser/autocomplete_provider_client.h" | 14 #include "components/omnibox/browser/autocomplete_provider_client.h" |
| 10 #include "components/omnibox/browser/autocomplete_provider_listener.h" | 15 #include "components/omnibox/browser/autocomplete_provider_listener.h" |
| 16 #include "components/omnibox/browser/autocomplete_provider_utils.h" |
| 11 #include "components/omnibox/browser/history_url_provider.h" | 17 #include "components/omnibox/browser/history_url_provider.h" |
| 12 #include "components/omnibox/browser/physical_web_provider.h" | 18 #include "components/omnibox/browser/omnibox_field_trial.h" |
| 19 #include "components/omnibox/browser/physical_web_node.h" |
| 20 #include "components/omnibox/browser/url_prefix.h" |
| 13 #include "components/omnibox/browser/verbatim_match.h" | 21 #include "components/omnibox/browser/verbatim_match.h" |
| 14 #include "components/physical_web/data_source/physical_web_data_source.h" | 22 #include "components/physical_web/data_source/physical_web_data_source.h" |
| 15 #include "components/url_formatter/url_formatter.h" | 23 #include "components/url_formatter/url_formatter.h" |
| 16 #include "grit/components_strings.h" | 24 #include "grit/components_strings.h" |
| 17 #include "ui/base/l10n/l10n_util.h" | 25 #include "ui/base/l10n/l10n_util.h" |
| 18 #include "ui/gfx/text_elider.h" | 26 #include "ui/gfx/text_elider.h" |
| 19 #include "url/gurl.h" | 27 #include "url/gurl.h" |
| 20 | 28 |
| 21 namespace { | 29 namespace { |
| 22 | 30 |
| 23 // Relevance score of the first Physical Web URL autocomplete match. This score | |
| 24 // is intended to be between ClipboardURLProvider and ZeroSuggestProvider. | |
| 25 // Subsequent matches should decrease in relevance to preserve the ordering | |
| 26 // in the metadata list. | |
| 27 static const int kPhysicalWebUrlBaseRelevance = 700; | |
| 28 | |
| 29 // The maximum length of the page title's part of the overflow item's | 31 // The maximum length of the page title's part of the overflow item's |
| 30 // description. Longer titles will be truncated to this length. In a normal | 32 // description. Longer titles will be truncated to this length. In a normal |
| 31 // Physical Web match item (non-overflow item) we allow the omnibox display to | 33 // Physical Web match item (non-overflow item) we allow the omnibox display to |
| 32 // truncate the title instead. | 34 // truncate the title instead. |
| 33 static const size_t kMaxTitleLengthInOverflow = 15; | 35 static const size_t kMaxTitleLengthInOverflow = 15; |
| 36 |
| 37 // The maximum number of Physical Web URLs to retrieve from the index. |
| 38 static const size_t kPhysicalWebIndexMaxMatches = 50; |
| 39 |
| 34 } | 40 } |
| 35 | 41 |
| 36 // static | 42 // static |
| 37 const size_t PhysicalWebProvider::kPhysicalWebMaxMatches = 1; | 43 const size_t PhysicalWebProvider::kPhysicalWebMaxMatches = 1; |
| 38 | 44 |
| 39 // static | 45 // static |
| 40 PhysicalWebProvider* PhysicalWebProvider::Create( | 46 PhysicalWebProvider* PhysicalWebProvider::Create( |
| 41 AutocompleteProviderClient* client, | 47 AutocompleteProviderClient* client, |
| 42 HistoryURLProvider* history_url_provider) { | 48 HistoryURLProvider* history_url_provider) { |
| 43 return new PhysicalWebProvider(client, history_url_provider); | 49 return new PhysicalWebProvider(client, history_url_provider); |
| 44 } | 50 } |
| 45 | 51 |
| 46 void PhysicalWebProvider::Start(const AutocompleteInput& input, | 52 void PhysicalWebProvider::Start(const AutocompleteInput& input, |
| 47 bool minimal_changes) { | 53 bool minimal_changes) { |
| 48 DCHECK(kPhysicalWebMaxMatches < kMaxMatches); | 54 DCHECK(kPhysicalWebMaxMatches < kMaxMatches); |
| 49 | 55 |
| 50 Stop(false, false); | 56 Stop(false, false); |
| 51 | 57 |
| 52 done_ = false; | 58 done_ = false; |
| 53 matches_.clear(); | 59 matches_.clear(); |
| 54 | 60 |
| 55 // Stop providing suggestions when the user enters text into the omnibox. | 61 had_physical_web_suggestions_ = false; |
| 56 if (!input.from_omnibox_focus()) { | 62 if (input.from_omnibox_focus()) |
| 57 done_ = true; | 63 had_physical_web_suggestions_at_focus_or_later_ = false; |
| 58 return; | |
| 59 } | |
| 60 | 64 |
| 61 // Don't provide suggestions in incognito mode. | 65 // Don't provide suggestions in incognito mode. |
| 62 if (client_->IsOffTheRecord()) { | 66 if (client_->IsOffTheRecord()) { |
| 63 done_ = true; | 67 done_ = true; |
| 64 nearby_url_count_ = 0; | 68 nearby_url_count_ = 0; |
| 65 return; | 69 return; |
| 66 } | 70 } |
| 67 | 71 |
| 68 physical_web::PhysicalWebDataSource* data_source = | 72 physical_web::PhysicalWebDataSource* data_source = |
| 69 client_->GetPhysicalWebDataSource(); | 73 client_->GetPhysicalWebDataSource(); |
| 70 if (!data_source) { | 74 if (!data_source) { |
| 71 done_ = true; | 75 done_ = true; |
| 72 nearby_url_count_ = 0; | 76 nearby_url_count_ = 0; |
| 73 return; | 77 return; |
| 74 } | 78 } |
| 75 | 79 |
| 76 ConstructMatches(data_source->GetMetadata().get()); | 80 if (input.from_omnibox_focus()) { |
| 81 ConstructZeroSuggestMatches(data_source->GetMetadata()); |
| 77 | 82 |
| 78 // Physical Web matches should never be default. If the omnibox input is | 83 if (!matches_.empty()) { |
| 79 // non-empty and we have at least one Physical Web match, add the current URL | 84 had_physical_web_suggestions_ = true; |
| 80 // as the default so that hitting enter after focusing the omnibox causes the | 85 had_physical_web_suggestions_at_focus_or_later_ = true; |
| 81 // current page to reload. If the input field is empty, no default match is | 86 } |
| 82 // required. | 87 |
| 83 if (!matches_.empty() && !input.text().empty()) { | 88 if (!zero_suggest_enabled_) { |
| 84 matches_.push_back(VerbatimMatchForURL(client_, input, input.current_url(), | 89 matches_.clear(); |
| 85 history_url_provider_, -1)); | 90 } |
| 91 |
| 92 // In zero-suggest, Physical Web matches should never be default. If the |
| 93 // omnibox input is non-empty and we have at least one Physical Web match, |
| 94 // add the current URL as the default so that hitting enter after focusing |
| 95 // the omnibox causes the current page to reload. If the input field is |
| 96 // empty, no default match is required. |
| 97 if (!matches_.empty() && !input.text().empty()) { |
| 98 matches_.push_back( |
| 99 VerbatimMatchForURL(client_, input, input.current_url(), |
| 100 history_url_provider_, -1)); |
| 101 } |
| 102 } else { |
| 103 ConstructQuerySuggestMatches(data_source->GetMetadata(), input); |
| 104 |
| 105 if (!matches_.empty()) { |
| 106 had_physical_web_suggestions_ = true; |
| 107 had_physical_web_suggestions_at_focus_or_later_ = true; |
| 108 } |
| 109 |
| 110 if (!after_typing_enabled_) { |
| 111 matches_.clear(); |
| 112 } |
| 86 } | 113 } |
| 87 | 114 |
| 88 done_ = true; | 115 done_ = true; |
| 89 } | 116 } |
| 90 | 117 |
| 91 void PhysicalWebProvider::Stop(bool clear_cached_results, | 118 void PhysicalWebProvider::Stop(bool clear_cached_results, |
| 92 bool due_to_user_inactivity) { | 119 bool due_to_user_inactivity) { |
| 93 done_ = true; | 120 done_ = true; |
| 94 } | 121 } |
| 95 | 122 |
| 96 void PhysicalWebProvider::AddProviderInfo(ProvidersInfo* provider_info) const { | 123 void PhysicalWebProvider::AddProviderInfo(ProvidersInfo* provider_info) const { |
| 97 // AddProviderInfo is called for each autocomplete provider to allow | 124 // Record whether the provider could have provided a Physical Web suggestion, |
| 98 // provider-specific diagnostic info to be added to the omnibox log entry. | 125 // even if the suggestion could not be displayed due to the current field |
| 99 // In this case we do not append any diagnostic info and are taking advantage | 126 // trial. |
| 100 // of the fact that this method is only called when the user has accepted an | 127 provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo()); |
| 101 // autocomplete suggestion. | 128 metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back(); |
| 129 new_entry.set_provider(AsOmniboxEventProviderType()); |
| 130 new_entry.set_provider_done(done_); |
| 131 std::vector<uint32_t> field_trial_hashes; |
| 132 OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes); |
| 133 for (size_t i = 0; i < field_trial_hashes.size(); ++i) { |
| 134 if (had_physical_web_suggestions_) |
| 135 new_entry.mutable_field_trial_triggered()->Add(field_trial_hashes[i]); |
| 136 if (had_physical_web_suggestions_at_focus_or_later_) { |
| 137 new_entry.mutable_field_trial_triggered_in_session()->Add( |
| 138 field_trial_hashes[i]); |
| 139 } |
| 140 } |
| 102 | 141 |
| 103 // When the user accepts an autocomplete suggestion, record the number of | 142 // When the user accepts an autocomplete suggestion, record the number of |
| 104 // nearby Physical Web URLs at the time the provider last constructed matches. | 143 // nearby Physical Web URLs at the time the provider last constructed matches. |
| 105 UMA_HISTOGRAM_EXACT_LINEAR("Omnibox.SuggestionUsed.NearbyURLCount", | 144 UMA_HISTOGRAM_EXACT_LINEAR("Omnibox.SuggestionUsed.NearbyURLCount", |
| 106 nearby_url_count_, 50); | 145 nearby_url_count_, 50); |
| 107 } | 146 } |
| 108 | 147 |
| 109 PhysicalWebProvider::PhysicalWebProvider( | 148 PhysicalWebProvider::PhysicalWebProvider( |
| 110 AutocompleteProviderClient* client, | 149 AutocompleteProviderClient* client, |
| 111 HistoryURLProvider* history_url_provider) | 150 HistoryURLProvider* history_url_provider) |
| 112 : AutocompleteProvider(AutocompleteProvider::TYPE_PHYSICAL_WEB), | 151 : AutocompleteProvider(AutocompleteProvider::TYPE_PHYSICAL_WEB), |
| 113 client_(client), | 152 client_(client), |
| 114 history_url_provider_(history_url_provider) {} | 153 history_url_provider_(history_url_provider), |
| 154 zero_suggest_enabled_( |
| 155 OmniboxFieldTrial::InPhysicalWebZeroSuggestFieldTrial()), |
| 156 after_typing_enabled_( |
| 157 OmniboxFieldTrial::InPhysicalWebAfterTypingFieldTrial()), |
| 158 zero_suggest_base_relevance_( |
| 159 OmniboxFieldTrial::GetPhysicalWebZeroSuggestBaseRelevance()), |
| 160 after_typing_base_relevance_( |
| 161 OmniboxFieldTrial::GetPhysicalWebAfterTypingBaseRelevance()) {} |
| 115 | 162 |
| 116 PhysicalWebProvider::~PhysicalWebProvider() { | 163 PhysicalWebProvider::~PhysicalWebProvider() { |
| 117 } | 164 } |
| 118 | 165 |
| 119 void PhysicalWebProvider::ConstructMatches(base::ListValue* metadata_list) { | 166 void PhysicalWebProvider::ConstructZeroSuggestMatches( |
| 167 std::unique_ptr<base::ListValue> metadata_list) { |
| 120 nearby_url_count_ = metadata_list->GetSize(); | 168 nearby_url_count_ = metadata_list->GetSize(); |
| 121 size_t used_slots = 0; | 169 size_t used_slots = 0; |
| 122 | 170 |
| 123 for (size_t i = 0; i < nearby_url_count_; ++i) { | 171 for (size_t i = 0; i < nearby_url_count_; ++i) { |
| 124 base::DictionaryValue* metadata_item = NULL; | 172 base::DictionaryValue* metadata_item = NULL; |
| 125 if (!metadata_list->GetDictionary(i, &metadata_item)) { | 173 if (!metadata_list->GetDictionary(i, &metadata_item)) { |
| 126 continue; | 174 continue; |
| 127 } | 175 } |
| 128 | 176 |
| 129 std::string url_string; | 177 std::string url_string; |
| 130 std::string title_string; | 178 std::string title_string; |
| 131 if (!metadata_item->GetString(physical_web::kResolvedUrlKey, &url_string) || | 179 if (!metadata_item->GetString(physical_web::kResolvedUrlKey, &url_string) || |
| 132 !metadata_item->GetString(physical_web::kTitleKey, &title_string)) { | 180 !metadata_item->GetString(physical_web::kTitleKey, &title_string)) { |
| 133 continue; | 181 continue; |
| 134 } | 182 } |
| 135 base::string16 title = | 183 base::string16 title = |
| 136 AutocompleteMatch::SanitizeString(base::UTF8ToUTF16(title_string)); | 184 AutocompleteMatch::SanitizeString(base::UTF8ToUTF16(title_string)); |
| 137 | 185 |
| 138 // Add match items with decreasing relevance to preserve the ordering in | 186 // Add match items with decreasing relevance to preserve the ordering in |
| 139 // the metadata list. | 187 // the metadata list. |
| 140 int relevance = kPhysicalWebUrlBaseRelevance - used_slots; | 188 int relevance = zero_suggest_base_relevance_ - used_slots; |
| 141 | 189 |
| 142 // Append an overflow item if creating a match for each metadata item would | 190 // Append an overflow item if creating a match for each metadata item would |
| 143 // exceed the match limit. | 191 // exceed the match limit. |
| 144 const size_t remaining_slots = kPhysicalWebMaxMatches - used_slots; | 192 const size_t remaining_slots = kPhysicalWebMaxMatches - used_slots; |
| 145 const size_t remaining_metadata = nearby_url_count_ - i; | 193 const size_t remaining_metadata = nearby_url_count_ - i; |
| 146 if ((remaining_slots == 1) && (remaining_metadata > remaining_slots)) { | 194 if ((remaining_slots == 1) && (remaining_metadata > remaining_slots)) { |
| 147 AppendOverflowItem(remaining_metadata, relevance, title); | 195 AppendOverflowItem(remaining_metadata, relevance, title); |
| 148 break; | 196 break; |
| 149 } | 197 } |
| 150 | 198 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 171 ACMatchClassification(0, ACMatchClassification::NONE)); | 219 ACMatchClassification(0, ACMatchClassification::NONE)); |
| 172 | 220 |
| 173 matches_.push_back(match); | 221 matches_.push_back(match); |
| 174 ++used_slots; | 222 ++used_slots; |
| 175 } | 223 } |
| 176 | 224 |
| 177 UMA_HISTOGRAM_EXACT_LINEAR( | 225 UMA_HISTOGRAM_EXACT_LINEAR( |
| 178 "Omnibox.PhysicalWebProviderMatches", matches_.size(), kMaxMatches); | 226 "Omnibox.PhysicalWebProviderMatches", matches_.size(), kMaxMatches); |
| 179 } | 227 } |
| 180 | 228 |
| 229 void PhysicalWebProvider::ConstructQuerySuggestMatches( |
| 230 std::unique_ptr<base::ListValue> metadata_list, |
| 231 const AutocompleteInput& input) { |
| 232 // Passing nullptr for the TitledUrlNodeSorter will cause the returned match |
| 233 // list to be unsorted. |
| 234 bookmarks::TitledUrlIndex index(nullptr); |
| 235 std::vector<std::unique_ptr<PhysicalWebNode> > nodes; |
| 236 const size_t metadata_count = metadata_list->GetSize(); |
| 237 for (size_t i = 0; i < metadata_count; ++i) { |
| 238 base::DictionaryValue* metadata_item = NULL; |
| 239 if (metadata_list->GetDictionary(i, &metadata_item)) { |
| 240 nodes.push_back(base::MakeUnique<PhysicalWebNode>(*metadata_item)); |
| 241 index.Add(nodes.back().get()); |
| 242 } |
| 243 } |
| 244 |
| 245 std::vector<bookmarks::TitledUrlMatch> titled_url_matches; |
| 246 index.GetResultsMatching(input.text(), kPhysicalWebIndexMaxMatches, |
| 247 query_parser::MatchingAlgorithm::DEFAULT, |
| 248 &titled_url_matches); |
| 249 |
| 250 size_t used_slots = 0; |
| 251 const base::string16 fixed_up_input(FixupUserInput(input).second); |
| 252 for (auto titled_url_match : titled_url_matches) { |
| 253 const int relevance = after_typing_base_relevance_ - used_slots; |
| 254 matches_.push_back( |
| 255 TitledUrlMatchToAutocompleteMatch( |
| 256 this, client_->GetSchemeClassifier(), input, fixed_up_input, |
| 257 titled_url_match, AutocompleteMatchType::PHYSICAL_WEB, relevance)); |
| 258 ++used_slots; |
| 259 if (matches_.size() >= kPhysicalWebMaxMatches) { |
| 260 break; |
| 261 } |
| 262 } |
| 263 } |
| 264 |
| 181 void PhysicalWebProvider::AppendOverflowItem(int additional_url_count, | 265 void PhysicalWebProvider::AppendOverflowItem(int additional_url_count, |
| 182 int relevance, | 266 int relevance, |
| 183 const base::string16& title) { | 267 const base::string16& title) { |
| 184 std::string url_string = "chrome://physical-web"; | 268 std::string url_string = "chrome://physical-web"; |
| 185 GURL url(url_string); | 269 GURL url(url_string); |
| 186 | 270 |
| 187 AutocompleteMatch match(this, relevance, false, | 271 AutocompleteMatch match(this, relevance, false, |
| 188 AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW); | 272 AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW); |
| 189 match.destination_url = url; | 273 match.destination_url = url; |
| 190 | 274 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 205 AutocompleteInput::FormattedStringWithEquivalentMeaning( | 289 AutocompleteInput::FormattedStringWithEquivalentMeaning( |
| 206 url, match.contents, client_->GetSchemeClassifier()); | 290 url, match.contents, client_->GetSchemeClassifier()); |
| 207 | 291 |
| 208 match.description = | 292 match.description = |
| 209 l10n_util::GetStringUTF16(IDS_PHYSICAL_WEB_OVERFLOW_DESCRIPTION); | 293 l10n_util::GetStringUTF16(IDS_PHYSICAL_WEB_OVERFLOW_DESCRIPTION); |
| 210 match.description_class.push_back( | 294 match.description_class.push_back( |
| 211 ACMatchClassification(0, ACMatchClassification::NONE)); | 295 ACMatchClassification(0, ACMatchClassification::NONE)); |
| 212 | 296 |
| 213 matches_.push_back(match); | 297 matches_.push_back(match); |
| 214 } | 298 } |
| OLD | NEW |