Reland "Rename ClipboardURLProvider to ClipboardProvider"
This is a reland of e07980a114dfcd5bf3b9bc8d0df77f494b9463e8
The original CL collided with another one that used this enum case.
Original change's description:
> Rename ClipboardURLProvider to ClipboardProvider
>
> ClipboardURLProvider now provides matches for text and images on the
> clipboard as well as URLs. Therefore, we should rename it to just
> ClipboardProvider.
>
> Bug: 923443
> Change-Id: I43ab8036cf2b0c564ebc3588b4c2324628ab2ee7
> Reviewed-on: https://chromium-review.googlesource.com/c/1445401
> Reviewed-by: Brian White <[email protected]>
> Reviewed-by: Justin Donnelly <[email protected]>
> Commit-Queue: Robbie Gibson <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#632681}
Bug: 923443
Change-Id: Iccbf43e55637330c2a8817b24386e70c755874b6
Reviewed-on: https://chromium-review.googlesource.com/c/1475758
Commit-Queue: Robbie Gibson <[email protected]>
Reviewed-by: Justin Donnelly <[email protected]>
Reviewed-by: Brian White <[email protected]>
Cr-Commit-Position: refs/heads/master@{#633768}
diff --git a/components/omnibox/browser/clipboard_provider.cc b/components/omnibox/browser/clipboard_provider.cc
new file mode 100644
index 0000000..9b8d9454
--- /dev/null
+++ b/components/omnibox/browser/clipboard_provider.cc
@@ -0,0 +1,305 @@
+// Copyright 2015 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/clipboard_provider.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_provider_client.h"
+#include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/verbatim_match.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/url_formatter.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image_util.h"
+
+ClipboardProvider::ClipboardProvider(AutocompleteProviderClient* client,
+ AutocompleteProviderListener* listener,
+ HistoryURLProvider* history_url_provider,
+ ClipboardRecentContent* clipboard_content)
+ : AutocompleteProvider(AutocompleteProvider::TYPE_CLIPBOARD),
+ client_(client),
+ listener_(listener),
+ clipboard_content_(clipboard_content),
+ history_url_provider_(history_url_provider),
+ current_url_suggested_times_(0),
+ callback_weak_ptr_factory_(this) {
+ DCHECK(clipboard_content_);
+}
+
+ClipboardProvider::~ClipboardProvider() {}
+
+void ClipboardProvider::Start(const AutocompleteInput& input,
+ bool minimal_changes) {
+ matches_.clear();
+
+ // If the user started typing, do not offer clipboard based match.
+ if (!input.from_omnibox_focus()) {
+ return;
+ }
+
+ // Image matched was kicked off asynchronously, so proceed when that ends.
+ if (CreateImageMatch(input)) {
+ return;
+ }
+ base::Optional<AutocompleteMatch> optional_match = CreateURLMatch(input);
+ if (!optional_match) {
+ optional_match = CreateTextMatch(input);
+ }
+ // The clipboard does not contain any suggestions
+ if (!optional_match) {
+ return;
+ }
+
+ AddCreatedMatchWithTracking(input, std::move(optional_match).value(),
+ clipboard_content_->GetClipboardContentAge());
+}
+
+void ClipboardProvider::Stop(bool clear_cached_results,
+ bool due_to_user_inactivity) {
+ callback_weak_ptr_factory_.InvalidateWeakPtrs();
+ AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+}
+
+void ClipboardProvider::AddCreatedMatchWithTracking(
+ const AutocompleteInput& input,
+ const AutocompleteMatch& match,
+ const base::TimeDelta clipboard_contents_age) {
+ // Record the number of times the currently-offered URL has been suggested.
+ // This only works over this run of Chrome; if the URL was in the clipboard
+ // on a previous run, those offerings will not be counted.
+ if (match.destination_url == current_url_suggested_) {
+ current_url_suggested_times_++;
+ } else {
+ current_url_suggested_ = match.destination_url;
+ current_url_suggested_times_ = 1;
+ }
+
+ base::UmaHistogramSparse(
+ "Omnibox.ClipboardSuggestionShownNumTimes",
+ std::min(current_url_suggested_times_, static_cast<size_t>(20)));
+
+ // If the omnibox is not empty, add a default match.
+ // This match will be opened when the user presses "Enter".
+ if (!input.text().empty()) {
+ const base::string16 description =
+ (base::FeatureList::IsEnabled(omnibox::kDisplayTitleForCurrentUrl))
+ ? input.current_title()
+ : base::string16();
+ AutocompleteMatch verbatim_match =
+ VerbatimMatchForURL(client_, input, input.current_url(), description,
+ history_url_provider_, -1);
+ matches_.push_back(verbatim_match);
+ }
+ UMA_HISTOGRAM_BOOLEAN("Omnibox.ClipboardSuggestionShownWithCurrentURL",
+ !matches_.empty());
+ UMA_HISTOGRAM_LONG_TIMES_100("Omnibox.ClipboardSuggestionShownAge",
+ clipboard_contents_age);
+
+ matches_.emplace_back(match);
+}
+
+base::Optional<AutocompleteMatch> ClipboardProvider::CreateURLMatch(
+ const AutocompleteInput& input) {
+ // The clipboard does not contain a URL worth suggesting.
+ base::Optional<GURL> optional_gurl =
+ clipboard_content_->GetRecentURLFromClipboard();
+ if (!optional_gurl) {
+ return base::nullopt;
+ }
+ GURL url = std::move(optional_gurl).value();
+
+ // The URL on the page is the same as the URL in the clipboard. Don't
+ // bother suggesting it.
+ if (url == input.current_url())
+ return base::nullopt;
+
+ DCHECK(url.is_valid());
+
+ // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
+ AutocompleteMatch match(this, 800, false,
+ AutocompleteMatchType::CLIPBOARD_URL);
+ match.destination_url = url;
+ // Because the user did not type a related input to get this clipboard
+ // suggestion, preserve the subdomain so the user has extra context.
+ auto format_types = AutocompleteMatch::GetFormatTypes(false, true);
+ match.contents.assign(url_formatter::FormatUrl(
+ url, format_types, net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.contents.length(),
+ ACMatchClassification::URL, &match.contents_class);
+
+ match.description.assign(l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.description.length(),
+ ACMatchClassification::NONE, &match.description_class);
+
+ return match;
+}
+
+base::Optional<AutocompleteMatch> ClipboardProvider::CreateTextMatch(
+ const AutocompleteInput& input) {
+ // Only try text match if feature is enabled
+ if (!base::FeatureList::IsEnabled(
+ omnibox::kEnableClipboardProviderTextSuggestions)) {
+ return base::nullopt;
+ }
+
+ base::Optional<base::string16> optional_text =
+ clipboard_content_->GetRecentTextFromClipboard();
+ if (!optional_text) {
+ return base::nullopt;
+ }
+ base::string16 text = std::move(optional_text).value();
+
+ // The text in the clipboard is a url. We don't want to prompt the user to
+ // search for a url.
+ if (GURL(text).is_valid())
+ return base::nullopt;
+
+ DCHECK(!text.empty());
+
+ // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
+ AutocompleteMatch match(this, 800, false,
+ AutocompleteMatchType::CLIPBOARD_TEXT);
+ TemplateURLService* url_service = client_->GetTemplateURLService();
+ const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
+ DCHECK(!default_url->url().empty());
+ DCHECK(default_url->url_ref().IsValid(url_service->search_terms_data()));
+ TemplateURLRef::SearchTermsArgs search_args(text);
+ GURL result(default_url->url_ref().ReplaceSearchTerms(
+ search_args, url_service->search_terms_data()));
+
+ match.destination_url = result;
+ match.contents.assign(l10n_util::GetStringFUTF16(
+ IDS_COPIED_TEXT_FROM_CLIPBOARD, AutocompleteMatch::SanitizeString(text)));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.contents.length(),
+ ACMatchClassification::NONE, &match.contents_class);
+
+ match.description.assign(l10n_util::GetStringUTF16(IDS_TEXT_FROM_CLIPBOARD));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.description.length(),
+ ACMatchClassification::NONE, &match.description_class);
+
+ match.keyword = default_url->keyword();
+ match.transition = ui::PAGE_TRANSITION_GENERATED;
+
+ return match;
+}
+
+bool ClipboardProvider::CreateImageMatch(const AutocompleteInput& input) {
+ // Only try image match if feature is enabled
+ if (!base::FeatureList::IsEnabled(
+ omnibox::kEnableClipboardProviderImageSuggestions)) {
+ return false;
+ }
+
+ // Make sure current provider supports image search
+ TemplateURLService* url_service = client_->GetTemplateURLService();
+ const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
+
+ if (!default_url || default_url->image_url().empty() ||
+ !default_url->image_url_ref().IsValid(url_service->search_terms_data())) {
+ return false;
+ }
+
+ base::Optional<gfx::Image> optional_image =
+ clipboard_content_->GetRecentImageFromClipboard();
+ if (!optional_image) {
+ return false;
+ }
+
+ // We want to get the age here because the contents of the clipboard could
+ // change after this point. We want the age of the image we actually use, not
+ // the age of whatever's on the clipboard when the histogram is created (i.e
+ // when the match is created).
+ base::TimeDelta clipboard_contents_age =
+ clipboard_content_->GetClipboardContentAge();
+ done_ = false;
+ PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(&ClipboardProvider::EncodeClipboardImage,
+ optional_image.value()),
+ base::BindOnce(&ClipboardProvider::ConstructImageMatchCallback,
+ callback_weak_ptr_factory_.GetWeakPtr(), input,
+ url_service, clipboard_contents_age));
+ return true;
+}
+
+scoped_refptr<base::RefCountedMemory> ClipboardProvider::EncodeClipboardImage(
+ gfx::Image image) {
+ gfx::Image resized_image = gfx::ResizedImageForSearchByImage(image);
+ return resized_image.As1xPNGBytes();
+}
+
+void ClipboardProvider::ConstructImageMatchCallback(
+ const AutocompleteInput& input,
+ TemplateURLService* url_service,
+ base::TimeDelta clipboard_contents_age,
+ scoped_refptr<base::RefCountedMemory> image_bytes) {
+ const TemplateURL* default_url = url_service->GetDefaultSearchProvider();
+ // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
+ AutocompleteMatch match(this, 800, false,
+ AutocompleteMatchType::CLIPBOARD_IMAGE);
+
+ match.description.assign(l10n_util::GetStringUTF16(IDS_IMAGE_FROM_CLIPBOARD));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.description.length(),
+ ACMatchClassification::NONE, &match.description_class);
+
+ match.contents.assign(l10n_util::GetStringFUTF16(IDS_SEARCH_WEB_FOR_IMAGE,
+ default_url->short_name()));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.contents.length(),
+ ACMatchClassification::NONE, &match.contents_class);
+
+ TemplateURLRef::SearchTermsArgs search_args(base::ASCIIToUTF16(""));
+ search_args.image_thumbnail_content.assign(image_bytes->front_as<char>(),
+ image_bytes->size());
+ TemplateURLRef::PostContent post_content;
+ GURL result(default_url->image_url_ref().ReplaceSearchTerms(
+ search_args, url_service->search_terms_data(), &post_content));
+ match.destination_url = result;
+ match.post_content =
+ std::make_unique<TemplateURLRef::PostContent>(post_content);
+
+ match.transition = ui::PAGE_TRANSITION_GENERATED;
+
+ AddCreatedMatchWithTracking(input, match, clipboard_contents_age);
+ listener_->OnProviderUpdate(true);
+ done_ = true;
+}
+
+void ClipboardProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
+ // If a URL wasn't suggested on this most recent focus event, don't bother
+ // setting |times_returned_results_in_session|, as in effect this URL has
+ // never been suggested during the current session. (For the purpose of
+ // this provider, we define a session as intervals between when a URL
+ // clipboard suggestion changes.)
+ if (current_url_suggested_times_ == 0)
+ return;
+ provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
+ metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
+ new_entry.set_provider(AsOmniboxEventProviderType());
+ new_entry.set_times_returned_results_in_session(current_url_suggested_times_);
+}