blob: 2d54dd284850fceefcc0f0104e8d9fe324e75a09 [file] [log] [blame]
sdefresne70948d62015-08-11 10:46:351// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Robbie Gibsonf4e78b82019-02-20 18:00:195#include "components/omnibox/browser/clipboard_provider.h"
sdefresne70948d62015-08-11 10:46:356
mpearsonea5016a2017-06-01 22:20:327#include <algorithm>
Robbie Gibson71b0c522019-02-13 00:05:588#include <memory>
9#include <utility>
Gang Wuba18dba2019-09-25 03:43:2110#include <vector>
mpearsonea5016a2017-06-01 22:20:3211
Robbie Gibson71b0c522019-02-13 00:05:5812#include "base/bind.h"
gcomanici8cabc77f2017-04-27 20:04:5413#include "base/feature_list.h"
Robbie Gibson71b0c522019-02-13 00:05:5814#include "base/memory/ref_counted_memory.h"
15#include "base/memory/weak_ptr.h"
Robbie Gibson1a9405b2019-02-21 12:20:4516#include "base/metrics/field_trial_params.h"
Ilya Sherman1edb6f182017-12-12 04:00:4217#include "base/metrics/histogram_functions.h"
mpearsonab3761492017-03-31 17:29:5118#include "base/metrics/histogram_macros.h"
Gang Wu730add3a2019-11-01 00:10:5519#include "base/metrics/user_metrics.h"
Robbie Gibsone6915ce12018-12-26 12:08:1020#include "base/optional.h"
sdefresne70948d62015-08-11 10:46:3521#include "base/strings/utf_string_conversions.h"
Robbie Gibson71b0c522019-02-13 00:05:5822#include "base/task/post_task.h"
Gabriel Charetteafbec8752020-05-29 04:34:1123#include "base/task/thread_pool.h"
Gang Wu3804fbc262020-03-24 00:50:1424#include "build/build_config.h"
sdefresne70948d62015-08-11 10:46:3525#include "components/omnibox/browser/autocomplete_input.h"
Robbie Gibson71b0c522019-02-13 00:05:5826#include "components/omnibox/browser/autocomplete_match.h"
sdefresne70948d62015-08-11 10:46:3527#include "components/omnibox/browser/autocomplete_provider_client.h"
Robbie Gibson71b0c522019-02-13 00:05:5828#include "components/omnibox/browser/autocomplete_provider_listener.h"
sdefresne70948d62015-08-11 10:46:3529#include "components/omnibox/browser/verbatim_match.h"
Tomasz Wiszkowskid938a1112019-03-06 18:01:5730#include "components/omnibox/common/omnibox_features.h"
sdefresne70948d62015-08-11 10:46:3531#include "components/open_from_clipboard/clipboard_recent_content.h"
Robbie Gibsone6915ce12018-12-26 12:08:1032#include "components/search_engines/template_url_service.h"
thakisfe8fa0a2017-02-23 19:46:3633#include "components/strings/grit/components_strings.h"
sdefresne70948d62015-08-11 10:46:3534#include "components/url_formatter/url_formatter.h"
sdefresne70948d62015-08-11 10:46:3535#include "ui/base/l10n/l10n_util.h"
Robbie Gibson71b0c522019-02-13 00:05:5836#include "ui/gfx/image/image_util.h"
sdefresne70948d62015-08-11 10:46:3537
Gang Wuba18dba2019-09-25 03:43:2138namespace {
39
Gang Wu0db1f1fa2019-10-02 20:30:1040const size_t kMaxClipboardSuggestionShownNumTimesSimpleSize = 20;
41
Gang Wu64198f2002020-06-25 00:28:2742// Clipboard suggestions should be placed above search and url suggestions, but
43// below query tiles.
44const int kClipboardMatchRelevanceScore = 1500;
45
Gang Wuba18dba2019-09-25 03:43:2146bool IsMatchDeletionEnabled() {
47 return base::FeatureList::IsEnabled(
48 omnibox::kOmniboxRemoveSuggestionsFromClipboard);
49}
50
Gang Wu0db1f1fa2019-10-02 20:30:1051void RecordCreatingClipboardSuggestionMetrics(
52 size_t current_url_suggested_times,
53 bool matches_is_empty,
54 AutocompleteMatchType::Type match_type,
55 const base::TimeDelta clipboard_contents_age) {
56 DCHECK(match_type == AutocompleteMatchType::CLIPBOARD_URL ||
57 match_type == AutocompleteMatchType::CLIPBOARD_TEXT ||
58 match_type == AutocompleteMatchType::CLIPBOARD_IMAGE);
59
60 base::UmaHistogramSparse(
61 "Omnibox.ClipboardSuggestionShownNumTimes",
62 std::min(current_url_suggested_times,
63 kMaxClipboardSuggestionShownNumTimesSimpleSize));
64 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL",
65 !matches_is_empty);
66 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge",
67 clipboard_contents_age);
68 if (match_type == AutocompleteMatchType::CLIPBOARD_URL) {
69 base::UmaHistogramSparse(
70 "Omnibox.ClipboardSuggestionShownNumTimes.URL",
71 std::min(current_url_suggested_times,
72 kMaxClipboardSuggestionShownNumTimesSimpleSize));
73 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL.URL",
74 !matches_is_empty);
75 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.URL",
76 clipboard_contents_age);
77 } else if (match_type == AutocompleteMatchType::CLIPBOARD_TEXT) {
78 base::UmaHistogramSparse(
79 "Omnibox.ClipboardSuggestionShownNumTimes.TEXT",
80 std::min(current_url_suggested_times,
81 kMaxClipboardSuggestionShownNumTimesSimpleSize));
82 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL.TEXT",
83 !matches_is_empty);
84 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.TEXT",
85 clipboard_contents_age);
86 } else if (match_type == AutocompleteMatchType::CLIPBOARD_IMAGE) {
87 base::UmaHistogramSparse(
88 "Omnibox.ClipboardSuggestionShownNumTimes.IMAGE",
89 std::min(current_url_suggested_times,
90 kMaxClipboardSuggestionShownNumTimesSimpleSize));
91 UMA_HISTOGRAM_BOOLEAN(
92 "Omnibox.ClipboardSuggestionShownWithCurrentURL.IMAGE",
93 !matches_is_empty);
94 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.IMAGE",
95 clipboard_contents_age);
96 }
97}
98
99void RecordDeletingClipboardSuggestionMetrics(
100 AutocompleteMatchType::Type match_type,
101 const base::TimeDelta clipboard_contents_age) {
Gang Wu730add3a2019-11-01 00:10:55102 base::RecordAction(
103 base::UserMetricsAction("Omnibox.ClipboardSuggestionRemoved"));
104
Gang Wu0db1f1fa2019-10-02 20:30:10105 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge",
106 clipboard_contents_age);
107 if (match_type == AutocompleteMatchType::CLIPBOARD_URL) {
108 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge.URL",
109 clipboard_contents_age);
110 } else if (match_type == AutocompleteMatchType::CLIPBOARD_TEXT) {
111 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge.TEXT",
112 clipboard_contents_age);
Gang Wu77cb1112020-04-06 23:21:43113 } else if (match_type == AutocompleteMatchType::CLIPBOARD_IMAGE) {
114 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge.IMAGE",
115 clipboard_contents_age);
Gang Wu0db1f1fa2019-10-02 20:30:10116 }
117}
118
Gang Wuba18dba2019-09-25 03:43:21119} // namespace
120
Robbie Gibsonf4e78b82019-02-20 18:00:19121ClipboardProvider::ClipboardProvider(AutocompleteProviderClient* client,
122 AutocompleteProviderListener* listener,
123 HistoryURLProvider* history_url_provider,
124 ClipboardRecentContent* clipboard_content)
125 : AutocompleteProvider(AutocompleteProvider::TYPE_CLIPBOARD),
sdefresne70948d62015-08-11 10:46:35126 client_(client),
Robbie Gibson71b0c522019-02-13 00:05:58127 listener_(listener),
mpearson931028c2016-07-01 18:55:11128 clipboard_content_(clipboard_content),
mpearsonea5016a2017-06-01 22:20:32129 history_url_provider_(history_url_provider),
Robbie Gibson71b0c522019-02-13 00:05:58130 current_url_suggested_times_(0),
Robbie Gibson1a9405b2019-02-21 12:20:45131 field_trial_triggered_(false),
Jeremy Roman5c341f6d2019-07-15 15:56:10132 field_trial_triggered_in_session_(false) {
sdefresne70948d62015-08-11 10:46:35133 DCHECK(clipboard_content_);
134}
135
Robbie Gibsonf4e78b82019-02-20 18:00:19136ClipboardProvider::~ClipboardProvider() {}
sdefresne70948d62015-08-11 10:46:35137
Robbie Gibsonf4e78b82019-02-20 18:00:19138void ClipboardProvider::Start(const AutocompleteInput& input,
139 bool minimal_changes) {
sdefresne70948d62015-08-11 10:46:35140 matches_.clear();
Robbie Gibson1a9405b2019-02-21 12:20:45141 field_trial_triggered_ = false;
jif3986ea502016-07-13 13:43:42142
143 // If the user started typing, do not offer clipboard based match.
Robbie Gibson200b1b62019-10-08 16:13:47144 if (!input.from_omnibox_focus())
sdefresne70948d62015-08-11 10:46:35145 return;
Robbie Gibson71b0c522019-02-13 00:05:58146
147 // Image matched was kicked off asynchronously, so proceed when that ends.
Robbie Gibson200b1b62019-10-08 16:13:47148 if (CreateImageMatch(input))
Robbie Gibson71b0c522019-02-13 00:05:58149 return;
Robbie Gibson200b1b62019-10-08 16:13:47150
Robbie Gibson71b0c522019-02-13 00:05:58151 base::Optional<AutocompleteMatch> optional_match = CreateURLMatch(input);
Robbie Gibson200b1b62019-10-08 16:13:47152 if (!optional_match)
Robbie Gibsone6915ce12018-12-26 12:08:10153 optional_match = CreateTextMatch(input);
Robbie Gibson200b1b62019-10-08 16:13:47154
Robbie Gibsone6915ce12018-12-26 12:08:10155 // The clipboard does not contain any suggestions
Robbie Gibson200b1b62019-10-08 16:13:47156 if (!optional_match)
mpearsonea5016a2017-06-01 22:20:32157 return;
sdefresne70948d62015-08-11 10:46:35158
Robbie Gibson50dce212019-02-19 16:42:25159 AddCreatedMatchWithTracking(input, std::move(optional_match).value(),
160 clipboard_content_->GetClipboardContentAge());
Robbie Gibson71b0c522019-02-13 00:05:58161}
162
Robbie Gibsonf4e78b82019-02-20 18:00:19163void ClipboardProvider::Stop(bool clear_cached_results,
164 bool due_to_user_inactivity) {
Robbie Gibson71b0c522019-02-13 00:05:58165 callback_weak_ptr_factory_.InvalidateWeakPtrs();
166 AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
167}
168
Gang Wuba18dba2019-09-25 03:43:21169void ClipboardProvider::DeleteMatch(const AutocompleteMatch& match) {
Gang Wu0db1f1fa2019-10-02 20:30:10170 RecordDeletingClipboardSuggestionMetrics(
171 match.type, clipboard_content_->GetClipboardContentAge());
Gang Wuba18dba2019-09-25 03:43:21172 clipboard_content_->ClearClipboardContent();
173
174 const auto pred = [&match](const AutocompleteMatch& i) {
175 return i.contents == match.contents && i.type == match.type;
176 };
177 base::EraseIf(matches_, pred);
178}
179
180void ClipboardProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
181 // If a URL wasn't suggested on this most recent focus event, don't bother
182 // setting |times_returned_results_in_session|, as in effect this URL has
183 // never been suggested during the current session. (For the purpose of
184 // this provider, we define a session as intervals between when a URL
185 // clipboard suggestion changes.)
186 if (current_url_suggested_times_ == 0)
187 return;
188 provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
189 metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
190 new_entry.set_provider(AsOmniboxEventProviderType());
191 new_entry.set_provider_done(done_);
192 new_entry.set_times_returned_results_in_session(current_url_suggested_times_);
193
194 if (field_trial_triggered_ || field_trial_triggered_in_session_) {
195 std::vector<uint32_t> field_trial_hashes;
196 OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes);
197 for (uint32_t trial : field_trial_hashes) {
198 if (field_trial_triggered_) {
199 new_entry.mutable_field_trial_triggered()->Add(trial);
200 }
201 if (field_trial_triggered_in_session_) {
202 new_entry.mutable_field_trial_triggered_in_session()->Add(trial);
203 }
204 }
205 }
206}
207
Robbie Gibson1a9405b2019-02-21 12:20:45208void ClipboardProvider::ResetSession() {
209 field_trial_triggered_ = false;
210 field_trial_triggered_in_session_ = false;
211}
212
Robbie Gibsonf4e78b82019-02-20 18:00:19213void ClipboardProvider::AddCreatedMatchWithTracking(
Robbie Gibson71b0c522019-02-13 00:05:58214 const AutocompleteInput& input,
Robbie Gibson50dce212019-02-19 16:42:25215 const AutocompleteMatch& match,
216 const base::TimeDelta clipboard_contents_age) {
Robbie Gibsond2a2eee2019-01-25 14:38:10217 // Record the number of times the currently-offered URL has been suggested.
218 // This only works over this run of Chrome; if the URL was in the clipboard
219 // on a previous run, those offerings will not be counted.
Robbie Gibson71b0c522019-02-13 00:05:58220 if (match.destination_url == current_url_suggested_) {
Robbie Gibsond2a2eee2019-01-25 14:38:10221 current_url_suggested_times_++;
222 } else {
Robbie Gibson71b0c522019-02-13 00:05:58223 current_url_suggested_ = match.destination_url;
Robbie Gibsond2a2eee2019-01-25 14:38:10224 current_url_suggested_times_ = 1;
225 }
226
Robbie Gibsond2a2eee2019-01-25 14:38:10227
jif3986ea502016-07-13 13:43:42228 // If the omnibox is not empty, add a default match.
229 // This match will be opened when the user presses "Enter".
230 if (!input.text().empty()) {
gcomanici8cabc77f2017-04-27 20:04:54231 const base::string16 description =
232 (base::FeatureList::IsEnabled(omnibox::kDisplayTitleForCurrentUrl))
233 ? input.current_title()
234 : base::string16();
235 AutocompleteMatch verbatim_match =
236 VerbatimMatchForURL(client_, input, input.current_url(), description,
237 history_url_provider_, -1);
sdefresne70948d62015-08-11 10:46:35238 matches_.push_back(verbatim_match);
jif3986ea502016-07-13 13:43:42239 }
Gang Wu0db1f1fa2019-10-02 20:30:10240
241 RecordCreatingClipboardSuggestionMetrics(current_url_suggested_times_,
242 matches_.empty(), match.type,
243 clipboard_contents_age);
Robbie Gibsone6915ce12018-12-26 12:08:10244
manuk21aa24d2019-05-01 15:08:16245 matches_.push_back(match);
Robbie Gibsone6915ce12018-12-26 12:08:10246}
247
Robbie Gibsonf4e78b82019-02-20 18:00:19248base::Optional<AutocompleteMatch> ClipboardProvider::CreateURLMatch(
Robbie Gibsone6915ce12018-12-26 12:08:10249 const AutocompleteInput& input) {
250 // The clipboard does not contain a URL worth suggesting.
251 base::Optional<GURL> optional_gurl =
252 clipboard_content_->GetRecentURLFromClipboard();
Robbie Gibson200b1b62019-10-08 16:13:47253 if (!optional_gurl)
Robbie Gibsone6915ce12018-12-26 12:08:10254 return base::nullopt;
Robbie Gibson200b1b62019-10-08 16:13:47255
Robbie Gibsone6915ce12018-12-26 12:08:10256 GURL url = std::move(optional_gurl).value();
257
258 // The URL on the page is the same as the URL in the clipboard. Don't
259 // bother suggesting it.
260 if (url == input.current_url())
261 return base::nullopt;
262
263 DCHECK(url.is_valid());
264
Gang Wu64198f2002020-06-25 00:28:27265 AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
266 IsMatchDeletionEnabled(),
Robbie Gibson3e74f7d2019-01-21 14:03:00267 AutocompleteMatchType::CLIPBOARD_URL);
sdefresne70948d62015-08-11 10:46:35268 match.destination_url = url;
Robbie Gibson66a41d92019-11-27 10:04:11269
Tommy C. Lic68f2e42017-12-19 20:43:45270 // Because the user did not type a related input to get this clipboard
Tommy C. Li21da43522018-11-20 16:35:28271 // suggestion, preserve the subdomain so the user has extra context.
272 auto format_types = AutocompleteMatch::GetFormatTypes(false, true);
tommycli72014f62017-06-29 21:42:16273 match.contents.assign(url_formatter::FormatUrl(
274 url, format_types, net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
manuk21aa24d2019-05-01 15:08:16275 if (!match.contents.empty())
276 match.contents_class.push_back({0, ACMatchClassification::URL});
Robbie Gibson66a41d92019-11-27 10:04:11277 match.fill_into_edit =
278 AutocompleteInput::FormattedStringWithEquivalentMeaning(
279 url, match.contents, client_->GetSchemeClassifier(), nullptr);
sdefresne70948d62015-08-11 10:46:35280
281 match.description.assign(l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16282 if (!match.description.empty())
283 match.description_class.push_back({0, ACMatchClassification::NONE});
sdefresne70948d62015-08-11 10:46:35284
Robbie Gibsone6915ce12018-12-26 12:08:10285 return match;
286}
287
Robbie Gibsonf4e78b82019-02-20 18:00:19288base::Optional<AutocompleteMatch> ClipboardProvider::CreateTextMatch(
Robbie Gibsone6915ce12018-12-26 12:08:10289 const AutocompleteInput& input) {
Robbie Gibsone6915ce12018-12-26 12:08:10290 base::Optional<base::string16> optional_text =
291 clipboard_content_->GetRecentTextFromClipboard();
Robbie Gibson200b1b62019-10-08 16:13:47292 if (!optional_text)
Robbie Gibsone6915ce12018-12-26 12:08:10293 return base::nullopt;
Robbie Gibson200b1b62019-10-08 16:13:47294
Robbie Gibsone6915ce12018-12-26 12:08:10295 base::string16 text = std::move(optional_text).value();
296
Robbie Gibson146bab02019-04-02 22:29:34297 // The clipboard can contain the empty string, which shouldn't be suggested.
Robbie Gibson200b1b62019-10-08 16:13:47298 if (text.empty())
Robbie Gibson146bab02019-04-02 22:29:34299 return base::nullopt;
Robbie Gibson146bab02019-04-02 22:29:34300
Robbie Gibsond2a2eee2019-01-25 14:38:10301 // The text in the clipboard is a url. We don't want to prompt the user to
302 // search for a url.
303 if (GURL(text).is_valid())
304 return base::nullopt;
305
Gang Wu64198f2002020-06-25 00:28:27306 AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
307 IsMatchDeletionEnabled(),
Robbie Gibson3e74f7d2019-01-21 14:03:00308 AutocompleteMatchType::CLIPBOARD_TEXT);
Robbie Gibson66a41d92019-11-27 10:04:11309 match.fill_into_edit = text;
310
Robbie Gibsone6915ce12018-12-26 12:08:10311 TemplateURLService* url_service = client_->GetTemplateURLService();
312 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
Robbie Gibson200b1b62019-10-08 16:13:47313 if (!default_url)
314 return base::nullopt;
315
Robbie Gibsone6915ce12018-12-26 12:08:10316 DCHECK(!default_url->url().empty());
317 DCHECK(default_url->url_ref().IsValid(url_service->search_terms_data()));
318 TemplateURLRef::SearchTermsArgs search_args(text);
319 GURL result(default_url->url_ref().ReplaceSearchTerms(
320 search_args, url_service->search_terms_data()));
Robbie Gibsond2a2eee2019-01-25 14:38:10321
Robbie Gibsone6915ce12018-12-26 12:08:10322 match.destination_url = result;
323 match.contents.assign(l10n_util::GetStringFUTF16(
324 IDS_COPIED_TEXT_FROM_CLIPBOARD, AutocompleteMatch::SanitizeString(text)));
manuk21aa24d2019-05-01 15:08:16325 if (!match.contents.empty())
326 match.contents_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone6915ce12018-12-26 12:08:10327
328 match.description.assign(l10n_util::GetStringUTF16(IDS_TEXT_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16329 if (!match.description.empty())
330 match.description_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone6915ce12018-12-26 12:08:10331
Robbie Gibson71b0c522019-02-13 00:05:58332 match.keyword = default_url->keyword();
333 match.transition = ui::PAGE_TRANSITION_GENERATED;
334
Robbie Gibsone6915ce12018-12-26 12:08:10335 return match;
sdefresne70948d62015-08-11 10:46:35336}
mpearsonea5016a2017-06-01 22:20:32337
Robbie Gibsonf4e78b82019-02-20 18:00:19338bool ClipboardProvider::CreateImageMatch(const AutocompleteInput& input) {
Robbie Gibsone13f2e62019-01-09 11:00:24339 // Only try image match if feature is enabled
Robbie Gibson80d732e2019-01-10 10:42:49340 if (!base::FeatureList::IsEnabled(
341 omnibox::kEnableClipboardProviderImageSuggestions)) {
Robbie Gibson71b0c522019-02-13 00:05:58342 return false;
Robbie Gibsone13f2e62019-01-09 11:00:24343 }
344
Gang Wub984d8952020-03-31 15:21:51345 if (!clipboard_content_->HasRecentImageFromClipboard()) {
Robbie Gibson85cef3182019-07-21 15:10:29346 return false;
Gang Wub984d8952020-03-31 15:21:51347 }
Robbie Gibson85cef3182019-07-21 15:10:29348
Robbie Gibsone13f2e62019-01-09 11:00:24349 // Make sure current provider supports image search
350 TemplateURLService* url_service = client_->GetTemplateURLService();
351 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
352
353 if (!default_url || default_url->image_url().empty() ||
354 !default_url->image_url_ref().IsValid(url_service->search_terms_data())) {
Robbie Gibson71b0c522019-02-13 00:05:58355 return false;
Robbie Gibsone13f2e62019-01-09 11:00:24356 }
357
Robbie Gibson50dce212019-02-19 16:42:25358 // We want to get the age here because the contents of the clipboard could
359 // change after this point. We want the age of the image we actually use, not
360 // the age of whatever's on the clipboard when the histogram is created (i.e
361 // when the match is created).
362 base::TimeDelta clipboard_contents_age =
363 clipboard_content_->GetClipboardContentAge();
Gang Wub984d8952020-03-31 15:21:51364 clipboard_content_->GetRecentImageFromClipboard(
365 base::BindOnce(&ClipboardProvider::OnReceiveImage,
366 callback_weak_ptr_factory_.GetWeakPtr(), input,
367 url_service, clipboard_contents_age));
368 return true;
369}
370
371void ClipboardProvider::OnReceiveImage(
372 const AutocompleteInput& input,
373 TemplateURLService* url_service,
374 base::TimeDelta clipboard_contents_age,
375 base::Optional<gfx::Image> optional_image) {
376 if (!optional_image)
377 return;
Robbie Gibson71b0c522019-02-13 00:05:58378 done_ = false;
Gabriel Charetteafbec8752020-05-29 04:34:11379 base::ThreadPool::PostTaskAndReplyWithResult(
Robbie Gibson71b0c522019-02-13 00:05:58380 FROM_HERE,
Robbie Gibsonf4e78b82019-02-20 18:00:19381 base::BindOnce(&ClipboardProvider::EncodeClipboardImage,
Robbie Gibson71b0c522019-02-13 00:05:58382 optional_image.value()),
Robbie Gibsonf4e78b82019-02-20 18:00:19383 base::BindOnce(&ClipboardProvider::ConstructImageMatchCallback,
Robbie Gibson71b0c522019-02-13 00:05:58384 callback_weak_ptr_factory_.GetWeakPtr(), input,
Robbie Gibson50dce212019-02-19 16:42:25385 url_service, clipboard_contents_age));
Robbie Gibson71b0c522019-02-13 00:05:58386}
387
Robbie Gibsonf4e78b82019-02-20 18:00:19388scoped_refptr<base::RefCountedMemory> ClipboardProvider::EncodeClipboardImage(
389 gfx::Image image) {
Robbie Gibson71b0c522019-02-13 00:05:58390 gfx::Image resized_image = gfx::ResizedImageForSearchByImage(image);
391 return resized_image.As1xPNGBytes();
392}
393
Robbie Gibsonf4e78b82019-02-20 18:00:19394void ClipboardProvider::ConstructImageMatchCallback(
Robbie Gibson71b0c522019-02-13 00:05:58395 const AutocompleteInput& input,
396 TemplateURLService* url_service,
Robbie Gibson50dce212019-02-19 16:42:25397 base::TimeDelta clipboard_contents_age,
Robbie Gibson71b0c522019-02-13 00:05:58398 scoped_refptr<base::RefCountedMemory> image_bytes) {
399 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
Robbie Gibson200b1b62019-10-08 16:13:47400 DCHECK(default_url);
Gang Wu64198f2002020-06-25 00:28:27401
402 AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
403 IsMatchDeletionEnabled(),
Robbie Gibson3e74f7d2019-01-21 14:03:00404 AutocompleteMatchType::CLIPBOARD_IMAGE);
Robbie Gibsone13f2e62019-01-09 11:00:24405
406 match.description.assign(l10n_util::GetStringUTF16(IDS_IMAGE_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16407 if (!match.description.empty())
408 match.description_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone13f2e62019-01-09 11:00:24409
Robbie Gibson66a41d92019-11-27 10:04:11410 // This will end up being something like "Search for Copied Image." This may
411 // seem strange to use for |fill_into_edit, but it is because iOS requires
412 // some text in the text field for the Enter key to work when using keyboard
413 // navigation.
414 match.fill_into_edit = match.description;
415
Gang Wuc0173f12020-05-27 06:10:15416 match.search_terms_args =
417 std::make_unique<TemplateURLRef::SearchTermsArgs>(base::ASCIIToUTF16(""));
418 match.search_terms_args->image_thumbnail_content.assign(
419 image_bytes->front_as<char>(), image_bytes->size());
Robbie Gibson71b0c522019-02-13 00:05:58420 TemplateURLRef::PostContent post_content;
421 GURL result(default_url->image_url_ref().ReplaceSearchTerms(
Gang Wuc0173f12020-05-27 06:10:15422 *match.search_terms_args.get(), url_service->search_terms_data(),
423 &post_content));
424
425 if (!base::GetFieldTrialParamByFeatureAsBool(
426 omnibox::kEnableClipboardProviderImageSuggestions,
427 OmniboxFieldTrial::kImageSearchSuggestionThumbnail, false)) {
428 // If Omnibox image suggestion do not need thumbnail, release memory.
429 match.search_terms_args.reset();
430 }
Robbie Gibson71b0c522019-02-13 00:05:58431 match.destination_url = result;
432 match.post_content =
433 std::make_unique<TemplateURLRef::PostContent>(post_content);
434
Gang Wu64198f2002020-06-25 00:28:27435 match.keyword = default_url->keyword();
Robbie Gibson71b0c522019-02-13 00:05:58436 match.transition = ui::PAGE_TRANSITION_GENERATED;
437
Tommy Lic36fbf82019-09-09 18:55:53438 field_trial_triggered_ = true;
439 field_trial_triggered_in_session_ = true;
440 done_ = true;
441
Robbie Gibson1a9405b2019-02-21 12:20:45442 // Some users may be in a counterfactual study arm in which we perform all
443 // necessary work but do not forward the autocomplete matches.
444 bool in_counterfactual_group = base::GetFieldTrialParamByFeatureAsBool(
445 omnibox::kEnableClipboardProviderImageSuggestions,
446 "ClipboardProviderImageSuggestionsCounterfactualArm", false);
447 if (!in_counterfactual_group) {
448 AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
449 listener_->OnProviderUpdate(true);
450 }
Robbie Gibsone13f2e62019-01-09 11:00:24451}