blob: 8cab614326573ccb2e9f3ac714a5c8f5722a0544 [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"
23#include "base/task/task_traits.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 Wuba18dba2019-09-25 03:43:2142bool IsMatchDeletionEnabled() {
43 return base::FeatureList::IsEnabled(
44 omnibox::kOmniboxRemoveSuggestionsFromClipboard);
45}
46
Gang Wu0db1f1fa2019-10-02 20:30:1047void RecordCreatingClipboardSuggestionMetrics(
48 size_t current_url_suggested_times,
49 bool matches_is_empty,
50 AutocompleteMatchType::Type match_type,
51 const base::TimeDelta clipboard_contents_age) {
52 DCHECK(match_type == AutocompleteMatchType::CLIPBOARD_URL ||
53 match_type == AutocompleteMatchType::CLIPBOARD_TEXT ||
54 match_type == AutocompleteMatchType::CLIPBOARD_IMAGE);
55
56 base::UmaHistogramSparse(
57 "Omnibox.ClipboardSuggestionShownNumTimes",
58 std::min(current_url_suggested_times,
59 kMaxClipboardSuggestionShownNumTimesSimpleSize));
60 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL",
61 !matches_is_empty);
62 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge",
63 clipboard_contents_age);
64 if (match_type == AutocompleteMatchType::CLIPBOARD_URL) {
65 base::UmaHistogramSparse(
66 "Omnibox.ClipboardSuggestionShownNumTimes.URL",
67 std::min(current_url_suggested_times,
68 kMaxClipboardSuggestionShownNumTimesSimpleSize));
69 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL.URL",
70 !matches_is_empty);
71 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.URL",
72 clipboard_contents_age);
73 } else if (match_type == AutocompleteMatchType::CLIPBOARD_TEXT) {
74 base::UmaHistogramSparse(
75 "Omnibox.ClipboardSuggestionShownNumTimes.TEXT",
76 std::min(current_url_suggested_times,
77 kMaxClipboardSuggestionShownNumTimesSimpleSize));
78 UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL.TEXT",
79 !matches_is_empty);
80 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.TEXT",
81 clipboard_contents_age);
82 } else if (match_type == AutocompleteMatchType::CLIPBOARD_IMAGE) {
83 base::UmaHistogramSparse(
84 "Omnibox.ClipboardSuggestionShownNumTimes.IMAGE",
85 std::min(current_url_suggested_times,
86 kMaxClipboardSuggestionShownNumTimesSimpleSize));
87 UMA_HISTOGRAM_BOOLEAN(
88 "Omnibox.ClipboardSuggestionShownWithCurrentURL.IMAGE",
89 !matches_is_empty);
90 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge.IMAGE",
91 clipboard_contents_age);
92 }
93}
94
95void RecordDeletingClipboardSuggestionMetrics(
96 AutocompleteMatchType::Type match_type,
97 const base::TimeDelta clipboard_contents_age) {
Gang Wu730add3a2019-11-01 00:10:5598 base::RecordAction(
99 base::UserMetricsAction("Omnibox.ClipboardSuggestionRemoved"));
100
Gang Wu0db1f1fa2019-10-02 20:30:10101 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge",
102 clipboard_contents_age);
103 if (match_type == AutocompleteMatchType::CLIPBOARD_URL) {
104 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge.URL",
105 clipboard_contents_age);
106 } else if (match_type == AutocompleteMatchType::CLIPBOARD_TEXT) {
107 UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionRemovedAge.TEXT",
108 clipboard_contents_age);
109 }
110}
111
Gang Wuba18dba2019-09-25 03:43:21112} // namespace
113
Robbie Gibsonf4e78b82019-02-20 18:00:19114ClipboardProvider::ClipboardProvider(AutocompleteProviderClient* client,
115 AutocompleteProviderListener* listener,
116 HistoryURLProvider* history_url_provider,
117 ClipboardRecentContent* clipboard_content)
118 : AutocompleteProvider(AutocompleteProvider::TYPE_CLIPBOARD),
sdefresne70948d62015-08-11 10:46:35119 client_(client),
Robbie Gibson71b0c522019-02-13 00:05:58120 listener_(listener),
mpearson931028c2016-07-01 18:55:11121 clipboard_content_(clipboard_content),
mpearsonea5016a2017-06-01 22:20:32122 history_url_provider_(history_url_provider),
Robbie Gibson71b0c522019-02-13 00:05:58123 current_url_suggested_times_(0),
Robbie Gibson1a9405b2019-02-21 12:20:45124 field_trial_triggered_(false),
Jeremy Roman5c341f6d2019-07-15 15:56:10125 field_trial_triggered_in_session_(false) {
sdefresne70948d62015-08-11 10:46:35126 DCHECK(clipboard_content_);
127}
128
Robbie Gibsonf4e78b82019-02-20 18:00:19129ClipboardProvider::~ClipboardProvider() {}
sdefresne70948d62015-08-11 10:46:35130
Robbie Gibsonf4e78b82019-02-20 18:00:19131void ClipboardProvider::Start(const AutocompleteInput& input,
132 bool minimal_changes) {
sdefresne70948d62015-08-11 10:46:35133 matches_.clear();
Robbie Gibson1a9405b2019-02-21 12:20:45134 field_trial_triggered_ = false;
jif3986ea502016-07-13 13:43:42135
136 // If the user started typing, do not offer clipboard based match.
Robbie Gibson200b1b62019-10-08 16:13:47137 if (!input.from_omnibox_focus())
sdefresne70948d62015-08-11 10:46:35138 return;
Robbie Gibson71b0c522019-02-13 00:05:58139
140 // Image matched was kicked off asynchronously, so proceed when that ends.
Robbie Gibson200b1b62019-10-08 16:13:47141 if (CreateImageMatch(input))
Robbie Gibson71b0c522019-02-13 00:05:58142 return;
Robbie Gibson200b1b62019-10-08 16:13:47143
Robbie Gibson71b0c522019-02-13 00:05:58144 base::Optional<AutocompleteMatch> optional_match = CreateURLMatch(input);
Robbie Gibson200b1b62019-10-08 16:13:47145 if (!optional_match)
Robbie Gibsone6915ce12018-12-26 12:08:10146 optional_match = CreateTextMatch(input);
Robbie Gibson200b1b62019-10-08 16:13:47147
Robbie Gibsone6915ce12018-12-26 12:08:10148 // The clipboard does not contain any suggestions
Robbie Gibson200b1b62019-10-08 16:13:47149 if (!optional_match)
mpearsonea5016a2017-06-01 22:20:32150 return;
sdefresne70948d62015-08-11 10:46:35151
Robbie Gibson50dce212019-02-19 16:42:25152 AddCreatedMatchWithTracking(input, std::move(optional_match).value(),
153 clipboard_content_->GetClipboardContentAge());
Robbie Gibson71b0c522019-02-13 00:05:58154}
155
Robbie Gibsonf4e78b82019-02-20 18:00:19156void ClipboardProvider::Stop(bool clear_cached_results,
157 bool due_to_user_inactivity) {
Robbie Gibson71b0c522019-02-13 00:05:58158 callback_weak_ptr_factory_.InvalidateWeakPtrs();
159 AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
160}
161
Gang Wuba18dba2019-09-25 03:43:21162void ClipboardProvider::DeleteMatch(const AutocompleteMatch& match) {
Gang Wu0db1f1fa2019-10-02 20:30:10163 RecordDeletingClipboardSuggestionMetrics(
164 match.type, clipboard_content_->GetClipboardContentAge());
Gang Wuba18dba2019-09-25 03:43:21165 clipboard_content_->ClearClipboardContent();
166
167 const auto pred = [&match](const AutocompleteMatch& i) {
168 return i.contents == match.contents && i.type == match.type;
169 };
170 base::EraseIf(matches_, pred);
171}
172
173void ClipboardProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
174 // If a URL wasn't suggested on this most recent focus event, don't bother
175 // setting |times_returned_results_in_session|, as in effect this URL has
176 // never been suggested during the current session. (For the purpose of
177 // this provider, we define a session as intervals between when a URL
178 // clipboard suggestion changes.)
179 if (current_url_suggested_times_ == 0)
180 return;
181 provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
182 metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
183 new_entry.set_provider(AsOmniboxEventProviderType());
184 new_entry.set_provider_done(done_);
185 new_entry.set_times_returned_results_in_session(current_url_suggested_times_);
186
187 if (field_trial_triggered_ || field_trial_triggered_in_session_) {
188 std::vector<uint32_t> field_trial_hashes;
189 OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes);
190 for (uint32_t trial : field_trial_hashes) {
191 if (field_trial_triggered_) {
192 new_entry.mutable_field_trial_triggered()->Add(trial);
193 }
194 if (field_trial_triggered_in_session_) {
195 new_entry.mutable_field_trial_triggered_in_session()->Add(trial);
196 }
197 }
198 }
199}
200
Robbie Gibson1a9405b2019-02-21 12:20:45201void ClipboardProvider::ResetSession() {
202 field_trial_triggered_ = false;
203 field_trial_triggered_in_session_ = false;
204}
205
Robbie Gibsonf4e78b82019-02-20 18:00:19206void ClipboardProvider::AddCreatedMatchWithTracking(
Robbie Gibson71b0c522019-02-13 00:05:58207 const AutocompleteInput& input,
Robbie Gibson50dce212019-02-19 16:42:25208 const AutocompleteMatch& match,
209 const base::TimeDelta clipboard_contents_age) {
Robbie Gibsond2a2eee2019-01-25 14:38:10210 // Record the number of times the currently-offered URL has been suggested.
211 // This only works over this run of Chrome; if the URL was in the clipboard
212 // on a previous run, those offerings will not be counted.
Robbie Gibson71b0c522019-02-13 00:05:58213 if (match.destination_url == current_url_suggested_) {
Robbie Gibsond2a2eee2019-01-25 14:38:10214 current_url_suggested_times_++;
215 } else {
Robbie Gibson71b0c522019-02-13 00:05:58216 current_url_suggested_ = match.destination_url;
Robbie Gibsond2a2eee2019-01-25 14:38:10217 current_url_suggested_times_ = 1;
218 }
219
Robbie Gibsond2a2eee2019-01-25 14:38:10220
jif3986ea502016-07-13 13:43:42221 // If the omnibox is not empty, add a default match.
222 // This match will be opened when the user presses "Enter".
223 if (!input.text().empty()) {
gcomanici8cabc77f2017-04-27 20:04:54224 const base::string16 description =
225 (base::FeatureList::IsEnabled(omnibox::kDisplayTitleForCurrentUrl))
226 ? input.current_title()
227 : base::string16();
228 AutocompleteMatch verbatim_match =
229 VerbatimMatchForURL(client_, input, input.current_url(), description,
230 history_url_provider_, -1);
sdefresne70948d62015-08-11 10:46:35231 matches_.push_back(verbatim_match);
jif3986ea502016-07-13 13:43:42232 }
Gang Wu0db1f1fa2019-10-02 20:30:10233
234 RecordCreatingClipboardSuggestionMetrics(current_url_suggested_times_,
235 matches_.empty(), match.type,
236 clipboard_contents_age);
Robbie Gibsone6915ce12018-12-26 12:08:10237
manuk21aa24d2019-05-01 15:08:16238 matches_.push_back(match);
Robbie Gibsone6915ce12018-12-26 12:08:10239}
240
Robbie Gibsonf4e78b82019-02-20 18:00:19241base::Optional<AutocompleteMatch> ClipboardProvider::CreateURLMatch(
Robbie Gibsone6915ce12018-12-26 12:08:10242 const AutocompleteInput& input) {
243 // The clipboard does not contain a URL worth suggesting.
244 base::Optional<GURL> optional_gurl =
245 clipboard_content_->GetRecentURLFromClipboard();
Robbie Gibson200b1b62019-10-08 16:13:47246 if (!optional_gurl)
Robbie Gibsone6915ce12018-12-26 12:08:10247 return base::nullopt;
Robbie Gibson200b1b62019-10-08 16:13:47248
Robbie Gibsone6915ce12018-12-26 12:08:10249 GURL url = std::move(optional_gurl).value();
250
251 // The URL on the page is the same as the URL in the clipboard. Don't
252 // bother suggesting it.
253 if (url == input.current_url())
254 return base::nullopt;
255
256 DCHECK(url.is_valid());
257
jif3986ea502016-07-13 13:43:42258 // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
Gang Wuba18dba2019-09-25 03:43:21259 AutocompleteMatch match(this, 800, IsMatchDeletionEnabled(),
Robbie Gibson3e74f7d2019-01-21 14:03:00260 AutocompleteMatchType::CLIPBOARD_URL);
sdefresne70948d62015-08-11 10:46:35261 match.destination_url = url;
Robbie Gibson66a41d92019-11-27 10:04:11262
Tommy C. Lic68f2e42017-12-19 20:43:45263 // Because the user did not type a related input to get this clipboard
Tommy C. Li21da43522018-11-20 16:35:28264 // suggestion, preserve the subdomain so the user has extra context.
265 auto format_types = AutocompleteMatch::GetFormatTypes(false, true);
tommycli72014f62017-06-29 21:42:16266 match.contents.assign(url_formatter::FormatUrl(
267 url, format_types, net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
manuk21aa24d2019-05-01 15:08:16268 if (!match.contents.empty())
269 match.contents_class.push_back({0, ACMatchClassification::URL});
Robbie Gibson66a41d92019-11-27 10:04:11270 match.fill_into_edit =
271 AutocompleteInput::FormattedStringWithEquivalentMeaning(
272 url, match.contents, client_->GetSchemeClassifier(), nullptr);
sdefresne70948d62015-08-11 10:46:35273
274 match.description.assign(l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16275 if (!match.description.empty())
276 match.description_class.push_back({0, ACMatchClassification::NONE});
sdefresne70948d62015-08-11 10:46:35277
Robbie Gibsone6915ce12018-12-26 12:08:10278 return match;
279}
280
Robbie Gibsonf4e78b82019-02-20 18:00:19281base::Optional<AutocompleteMatch> ClipboardProvider::CreateTextMatch(
Robbie Gibsone6915ce12018-12-26 12:08:10282 const AutocompleteInput& input) {
Robbie Gibsone6915ce12018-12-26 12:08:10283 base::Optional<base::string16> optional_text =
284 clipboard_content_->GetRecentTextFromClipboard();
Robbie Gibson200b1b62019-10-08 16:13:47285 if (!optional_text)
Robbie Gibsone6915ce12018-12-26 12:08:10286 return base::nullopt;
Robbie Gibson200b1b62019-10-08 16:13:47287
Robbie Gibsone6915ce12018-12-26 12:08:10288 base::string16 text = std::move(optional_text).value();
289
Robbie Gibson146bab02019-04-02 22:29:34290 // The clipboard can contain the empty string, which shouldn't be suggested.
Robbie Gibson200b1b62019-10-08 16:13:47291 if (text.empty())
Robbie Gibson146bab02019-04-02 22:29:34292 return base::nullopt;
Robbie Gibson146bab02019-04-02 22:29:34293
Robbie Gibsond2a2eee2019-01-25 14:38:10294 // The text in the clipboard is a url. We don't want to prompt the user to
295 // search for a url.
296 if (GURL(text).is_valid())
297 return base::nullopt;
298
Robbie Gibsone6915ce12018-12-26 12:08:10299 // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
Gang Wuba18dba2019-09-25 03:43:21300 AutocompleteMatch match(this, 800, IsMatchDeletionEnabled(),
Robbie Gibson3e74f7d2019-01-21 14:03:00301 AutocompleteMatchType::CLIPBOARD_TEXT);
Robbie Gibson66a41d92019-11-27 10:04:11302 match.fill_into_edit = text;
303
Robbie Gibsone6915ce12018-12-26 12:08:10304 TemplateURLService* url_service = client_->GetTemplateURLService();
305 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
Robbie Gibson200b1b62019-10-08 16:13:47306 if (!default_url)
307 return base::nullopt;
308
Robbie Gibsone6915ce12018-12-26 12:08:10309 DCHECK(!default_url->url().empty());
310 DCHECK(default_url->url_ref().IsValid(url_service->search_terms_data()));
311 TemplateURLRef::SearchTermsArgs search_args(text);
312 GURL result(default_url->url_ref().ReplaceSearchTerms(
313 search_args, url_service->search_terms_data()));
Robbie Gibsond2a2eee2019-01-25 14:38:10314
Robbie Gibsone6915ce12018-12-26 12:08:10315 match.destination_url = result;
316 match.contents.assign(l10n_util::GetStringFUTF16(
317 IDS_COPIED_TEXT_FROM_CLIPBOARD, AutocompleteMatch::SanitizeString(text)));
manuk21aa24d2019-05-01 15:08:16318 if (!match.contents.empty())
319 match.contents_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone6915ce12018-12-26 12:08:10320
321 match.description.assign(l10n_util::GetStringUTF16(IDS_TEXT_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16322 if (!match.description.empty())
323 match.description_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone6915ce12018-12-26 12:08:10324
Robbie Gibson71b0c522019-02-13 00:05:58325 match.keyword = default_url->keyword();
326 match.transition = ui::PAGE_TRANSITION_GENERATED;
327
Robbie Gibsone6915ce12018-12-26 12:08:10328 return match;
sdefresne70948d62015-08-11 10:46:35329}
mpearsonea5016a2017-06-01 22:20:32330
Robbie Gibsonf4e78b82019-02-20 18:00:19331bool ClipboardProvider::CreateImageMatch(const AutocompleteInput& input) {
Robbie Gibsone13f2e62019-01-09 11:00:24332 // Only try image match if feature is enabled
Robbie Gibson80d732e2019-01-10 10:42:49333 if (!base::FeatureList::IsEnabled(
334 omnibox::kEnableClipboardProviderImageSuggestions)) {
Robbie Gibson71b0c522019-02-13 00:05:58335 return false;
Robbie Gibsone13f2e62019-01-09 11:00:24336 }
337
Robbie Gibson85cef3182019-07-21 15:10:29338 base::Optional<gfx::Image> optional_image =
339 clipboard_content_->GetRecentImageFromClipboard();
Robbie Gibson200b1b62019-10-08 16:13:47340 if (!optional_image)
Robbie Gibson85cef3182019-07-21 15:10:29341 return false;
Robbie Gibson85cef3182019-07-21 15:10:29342
Robbie Gibsone13f2e62019-01-09 11:00:24343 // Make sure current provider supports image search
344 TemplateURLService* url_service = client_->GetTemplateURLService();
345 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
346
347 if (!default_url || default_url->image_url().empty() ||
348 !default_url->image_url_ref().IsValid(url_service->search_terms_data())) {
Robbie Gibson71b0c522019-02-13 00:05:58349 return false;
Robbie Gibsone13f2e62019-01-09 11:00:24350 }
351
Robbie Gibson50dce212019-02-19 16:42:25352 // We want to get the age here because the contents of the clipboard could
353 // change after this point. We want the age of the image we actually use, not
354 // the age of whatever's on the clipboard when the histogram is created (i.e
355 // when the match is created).
356 base::TimeDelta clipboard_contents_age =
357 clipboard_content_->GetClipboardContentAge();
Robbie Gibson71b0c522019-02-13 00:05:58358 done_ = false;
359 PostTaskAndReplyWithResult(
360 FROM_HERE,
Robbie Gibsonf4e78b82019-02-20 18:00:19361 base::BindOnce(&ClipboardProvider::EncodeClipboardImage,
Robbie Gibson71b0c522019-02-13 00:05:58362 optional_image.value()),
Robbie Gibsonf4e78b82019-02-20 18:00:19363 base::BindOnce(&ClipboardProvider::ConstructImageMatchCallback,
Robbie Gibson71b0c522019-02-13 00:05:58364 callback_weak_ptr_factory_.GetWeakPtr(), input,
Robbie Gibson50dce212019-02-19 16:42:25365 url_service, clipboard_contents_age));
Robbie Gibson71b0c522019-02-13 00:05:58366 return true;
367}
368
Robbie Gibsonf4e78b82019-02-20 18:00:19369scoped_refptr<base::RefCountedMemory> ClipboardProvider::EncodeClipboardImage(
370 gfx::Image image) {
Robbie Gibson71b0c522019-02-13 00:05:58371 gfx::Image resized_image = gfx::ResizedImageForSearchByImage(image);
372 return resized_image.As1xPNGBytes();
373}
374
Robbie Gibsonf4e78b82019-02-20 18:00:19375void ClipboardProvider::ConstructImageMatchCallback(
Robbie Gibson71b0c522019-02-13 00:05:58376 const AutocompleteInput& input,
377 TemplateURLService* url_service,
Robbie Gibson50dce212019-02-19 16:42:25378 base::TimeDelta clipboard_contents_age,
Robbie Gibson71b0c522019-02-13 00:05:58379 scoped_refptr<base::RefCountedMemory> image_bytes) {
380 const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
Robbie Gibson200b1b62019-10-08 16:13:47381 DCHECK(default_url);
Robbie Gibsone13f2e62019-01-09 11:00:24382 // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
Robbie Gibson3e74f7d2019-01-21 14:03:00383 AutocompleteMatch match(this, 800, false,
384 AutocompleteMatchType::CLIPBOARD_IMAGE);
Robbie Gibsone13f2e62019-01-09 11:00:24385
386 match.description.assign(l10n_util::GetStringUTF16(IDS_IMAGE_FROM_CLIPBOARD));
manuk21aa24d2019-05-01 15:08:16387 if (!match.description.empty())
388 match.description_class.push_back({0, ACMatchClassification::NONE});
Robbie Gibsone13f2e62019-01-09 11:00:24389
Robbie Gibson66a41d92019-11-27 10:04:11390 // This will end up being something like "Search for Copied Image." This may
391 // seem strange to use for |fill_into_edit, but it is because iOS requires
392 // some text in the text field for the Enter key to work when using keyboard
393 // navigation.
394 match.fill_into_edit = match.description;
395
Robbie Gibson71b0c522019-02-13 00:05:58396 TemplateURLRef::SearchTermsArgs search_args(base::ASCIIToUTF16(""));
397 search_args.image_thumbnail_content.assign(image_bytes->front_as<char>(),
398 image_bytes->size());
399 TemplateURLRef::PostContent post_content;
400 GURL result(default_url->image_url_ref().ReplaceSearchTerms(
401 search_args, url_service->search_terms_data(), &post_content));
402 match.destination_url = result;
403 match.post_content =
404 std::make_unique<TemplateURLRef::PostContent>(post_content);
405
406 match.transition = ui::PAGE_TRANSITION_GENERATED;
407
Tommy Lic36fbf82019-09-09 18:55:53408 field_trial_triggered_ = true;
409 field_trial_triggered_in_session_ = true;
410 done_ = true;
411
Robbie Gibson1a9405b2019-02-21 12:20:45412 // Some users may be in a counterfactual study arm in which we perform all
413 // necessary work but do not forward the autocomplete matches.
414 bool in_counterfactual_group = base::GetFieldTrialParamByFeatureAsBool(
415 omnibox::kEnableClipboardProviderImageSuggestions,
416 "ClipboardProviderImageSuggestionsCounterfactualArm", false);
417 if (!in_counterfactual_group) {
418 AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
419 listener_->OnProviderUpdate(true);
420 }
Robbie Gibsone13f2e62019-01-09 11:00:24421}
422