blob: 357c437302deb68b8ccce60577ca40a76b93ba64 [file] [log] [blame]
// 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 <memory>
#include <string>
#include <utility>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/open_from_clipboard/fake_clipboard_recent_content.h"
#include "components/search_engines/omnibox_focus_type.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/template_url_service_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "url/gurl.h"
namespace {
const char kCurrentURL[] = "http://example.com/current";
const char kClipboardURL[] = "http://example.com/clipboard";
const char16_t kClipboardText[] = u"Search for me";
class CreateMatchWithContentCallbackWaiter {
public:
CreateMatchWithContentCallbackWaiter(
scoped_refptr<ClipboardProvider> provider,
AutocompleteMatch* match)
: received_(false) {
provider->UpdateClipboardMatchWithContent(
match, base::BindOnce(&CreateMatchWithContentCallbackWaiter::OnComplete,
weak_ptr_factory_.GetWeakPtr()));
}
void WaitForMatchUpdated() {
if (received_)
return;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
private:
void OnComplete() {
received_ = true;
if (quit_closure_)
std::move(quit_closure_).Run();
}
base::OnceClosure quit_closure_;
bool received_;
base::WeakPtrFactory<CreateMatchWithContentCallbackWaiter> weak_ptr_factory_{
this};
};
} // namespace
class ClipboardProviderTest : public testing::Test,
public AutocompleteProviderListener {
public:
ClipboardProviderTest()
: client_(new MockAutocompleteProviderClient()),
provider_(new ClipboardProvider(client_.get(),
this,
nullptr,
&clipboard_content_)) {
SetClipboardUrl(GURL(kClipboardURL));
}
~ClipboardProviderTest() override {}
void ClearClipboard() { clipboard_content_.SuppressClipboardContent(); }
void SetClipboardUrl(const GURL& url) {
clipboard_content_.SetClipboardURL(url, base::Minutes(9));
}
void SetClipboardText(const std::u16string& text) {
clipboard_content_.SetClipboardText(text, base::Minutes(9));
}
void SetClipboardImage(const gfx::Image& image) {
clipboard_content_.SetClipboardImage(image, base::Minutes(9));
}
bool IsClipboardEmpty() {
return clipboard_content_.GetRecentURLFromClipboard() == absl::nullopt &&
clipboard_content_.GetRecentTextFromClipboard() == absl::nullopt &&
!clipboard_content_.HasRecentImageFromClipboard();
}
AutocompleteInput CreateAutocompleteInput(OmniboxFocusType focus_type) {
AutocompleteInput input(std::u16string(), metrics::OmniboxEventProto::OTHER,
classifier_);
input.set_current_url(GURL(kCurrentURL));
input.set_focus_type(focus_type);
return input;
}
void MatchesImageCallback(absl::optional<AutocompleteMatch> match) {
matches_image_match_ = match;
}
protected:
// AutocompleteProviderListener:
void OnProviderUpdate(bool updated_matches) override;
TestSchemeClassifier classifier_;
FakeClipboardRecentContent clipboard_content_;
std::unique_ptr<MockAutocompleteProviderClient> client_;
scoped_refptr<ClipboardProvider> provider_;
absl::optional<AutocompleteMatch> matches_image_match_;
base::test::TaskEnvironment task_environment_;
};
void ClipboardProviderTest::OnProviderUpdate(bool updated_matches) {
// No action required.
}
TEST_F(ClipboardProviderTest, NotFromOmniboxFocus) {
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::DEFAULT), false);
EXPECT_TRUE(provider_->matches().empty());
}
TEST_F(ClipboardProviderTest, EmptyClipboard) {
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
ClearClipboard();
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
EXPECT_TRUE(provider_->matches().empty());
}
#if !defined(OS_ANDROID)
// The following tests do not apply to Android.
// On Android, the Omnibox won't access the content of the system clipboard
// before users click the reveal button, so the clipboard suggestions will be
// empty on start.
TEST_F(ClipboardProviderTest, ClipboardIsCurrentURL) {
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
SetClipboardUrl(GURL(kCurrentURL));
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
EXPECT_TRUE(provider_->matches().empty());
}
TEST_F(ClipboardProviderTest, HasMultipleMatches) {
EXPECT_CALL(*client_.get(), GetSchemeClassifier())
.WillOnce(testing::ReturnRef(classifier_));
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(GURL(kClipboardURL), provider_->matches().back().destination_url);
}
TEST_F(ClipboardProviderTest, MatchesUrl) {
SetClipboardUrl(GURL(kClipboardURL));
EXPECT_CALL(*client_.get(), GetSchemeClassifier())
.WillOnce(testing::ReturnRef(classifier_));
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(GURL(kClipboardURL), provider_->matches().back().destination_url);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_URL,
provider_->matches().back().type);
}
TEST_F(ClipboardProviderTest, MatchesText) {
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
SetClipboardText(kClipboardText);
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(kClipboardText, provider_->matches().back().contents);
EXPECT_EQ(kClipboardText, provider_->matches().back().fill_into_edit);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT,
provider_->matches().back().type);
}
TEST_F(ClipboardProviderTest, MatchesImage) {
auto template_url_service =
std::make_unique<TemplateURLService>(/*initializers=*/nullptr,
/*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
gfx::Image test_image = gfx::test::CreateImage(/*width=*/10, /*height=*/10);
scoped_refptr<base::RefCountedMemory> image_bytes =
provider_->EncodeClipboardImage(*test_image.ToImageSkia());
ASSERT_TRUE(image_bytes);
provider_->ConstructImageMatchCallback(
base::BindOnce(&ClipboardProviderTest::MatchesImageCallback,
base::Unretained(this)),
image_bytes);
ASSERT_TRUE(matches_image_match_);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_IMAGE, matches_image_match_->type);
}
#endif // !defined(OS_ANDROID)
TEST_F(ClipboardProviderTest, DeleteMatch) {
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
SetClipboardText(kClipboardText);
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_EQ(provider_->matches().size(), 1U);
provider_->DeleteMatch(provider_->matches().back());
ASSERT_EQ(provider_->matches().size(), 0U);
ASSERT_TRUE(IsClipboardEmpty());
}
TEST_F(ClipboardProviderTest, CreateBlankURLMatchOnStart) {
base::test::ScopedFeatureList feature_list;
base::Feature feature = omnibox::kClipboardSuggestionContentHidden;
feature_list.InitAndEnableFeature(feature);
SetClipboardUrl(GURL(kClipboardURL));
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_URL,
provider_->matches().back().type);
// Check the match is empty.
EXPECT_TRUE(provider_->matches().back().destination_url.is_empty());
}
TEST_F(ClipboardProviderTest, CreateBlankTextMatchOnStart) {
base::test::ScopedFeatureList feature_list;
base::Feature feature = omnibox::kClipboardSuggestionContentHidden;
feature_list.InitAndEnableFeature(feature);
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
SetClipboardText(kClipboardText);
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT,
provider_->matches().back().type);
// Check the match is empty.
EXPECT_TRUE(provider_->matches().back().contents.empty());
EXPECT_TRUE(provider_->matches().back().fill_into_edit.empty());
}
TEST_F(ClipboardProviderTest, CreateBlankImageMatchOnStart) {
base::test::ScopedFeatureList feature_list;
base::Feature feature = omnibox::kClipboardSuggestionContentHidden;
feature_list.InitAndEnableFeature(feature);
auto template_url_service =
std::make_unique<TemplateURLService>(/*initializers=*/nullptr,
/*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
gfx::Image test_image = gfx::test::CreateImage(/*width=*/10, /*height=*/10);
SetClipboardImage(test_image);
provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
ASSERT_GE(provider_->matches().size(), 1U);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_IMAGE,
provider_->matches().back().type);
EXPECT_FALSE(provider_->matches().back().post_content.get());
}
TEST_F(ClipboardProviderTest, CreateURLMatchWithContent) {
SetClipboardUrl(GURL(kClipboardURL));
EXPECT_CALL(*client_.get(), GetSchemeClassifier())
.WillOnce(testing::ReturnRef(classifier_));
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
AutocompleteMatch match = provider_->NewBlankURLMatch();
CreateMatchWithContentCallbackWaiter waiter(provider_, &match);
waiter.WaitForMatchUpdated();
EXPECT_EQ(GURL(kClipboardURL), match.destination_url);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_URL, match.type);
}
TEST_F(ClipboardProviderTest, CreateTextMatchWithContent) {
SetClipboardText(kClipboardText);
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
AutocompleteMatch match = provider_->NewBlankTextMatch();
CreateMatchWithContentCallbackWaiter waiter(provider_, &match);
waiter.WaitForMatchUpdated();
EXPECT_EQ(kClipboardText, match.contents);
EXPECT_EQ(kClipboardText, match.fill_into_edit);
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT, match.type);
}
TEST_F(ClipboardProviderTest, CreateImageMatchWithContent) {
gfx::Image test_image = gfx::test::CreateImage(/*width=*/10, /*height=*/10);
SetClipboardImage(test_image);
auto template_url_service = std::make_unique<TemplateURLService>(
/*initializers=*/nullptr, /*count=*/0);
client_->set_template_url_service(std::move(template_url_service));
AutocompleteMatch match = provider_->NewBlankImageMatch();
CreateMatchWithContentCallbackWaiter waiter(provider_, &match);
waiter.WaitForMatchUpdated();
EXPECT_FALSE(match.post_content->first.empty());
EXPECT_FALSE(match.post_content->second.empty());
EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_IMAGE, match.type);
}