blob: b33c072ea9f3ff9ed62976f0ed719bb2fd0d732c [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sharing/shared_clipboard/remote_copy_message_handler.h"
#include "chrome/browser/sharing/sharing_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/sharing_message/shared_clipboard/remote_copy_handle_message_result.h"
#include "components/sharing_message/sharing_constants.h"
#include "components/sharing_message/sharing_fcm_handler.h"
#include "components/sharing_message/sharing_service.h"
#include "content/public/test/browser_test.h"
#include "net/http/http_status_code.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/clipboard_observer.h"
#include "ui/base/clipboard/test/clipboard_test_util.h"
#include "ui/base/clipboard/test/test_clipboard.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/message_center/public/cpp/notification.h"
namespace {
const char kDeviceName[] = "test device name";
const char16_t kDeviceName16[] = u"test device name";
const char kText[] = "test text";
class ClipboardObserver : public ui::ClipboardObserver {
public:
explicit ClipboardObserver(base::RepeatingClosure callback)
: callback_(callback) {}
ClipboardObserver(const ClipboardObserver&) = delete;
ClipboardObserver& operator=(const ClipboardObserver&) = delete;
void OnClipboardDataChanged() override { callback_.Run(); }
private:
base::RepeatingClosure callback_;
};
} // namespace
// Browser tests for the Remote Copy feature.
class RemoteCopyBrowserTest : public InProcessBrowserTest {
public:
RemoteCopyBrowserTest() {
server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTP);
server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
EXPECT_TRUE(server_->Start());
}
~RemoteCopyBrowserTest() override = default;
void SetUpOnMainThread() override {
ui::TestClipboard::CreateForCurrentThread();
notification_tester_ = std::make_unique<NotificationDisplayServiceTester>(
browser()->profile());
sharing_service_ =
SharingServiceFactory::GetForBrowserContext(browser()->profile());
auto* remote_copy_handler = static_cast<RemoteCopyMessageHandler*>(
sharing_service_->GetSharingHandlerForTesting(
components_sharing_message::SharingMessage::kRemoteCopyMessage));
ASSERT_TRUE(remote_copy_handler);
remote_copy_handler->set_allowed_origin_for_testing(server_->base_url());
}
void TearDownOnMainThread() override {
notification_tester_.reset();
ui::Clipboard::DestroyClipboardForCurrentThread();
}
gcm::IncomingMessage CreateMessage(const std::string& device_name,
std::optional<std::string> text,
std::optional<GURL> image_url) {
components_sharing_message::SharingMessage sharing_message;
sharing_message.set_sender_guid(
base::Uuid::GenerateRandomV4().AsLowercaseString());
sharing_message.set_sender_device_name(device_name);
if (text) {
sharing_message.mutable_remote_copy_message()->set_text(text.value());
} else if (image_url) {
sharing_message.mutable_remote_copy_message()->set_image_url(
image_url.value().possibly_invalid_spec());
}
gcm::IncomingMessage incoming_message;
std::string serialized_sharing_message;
sharing_message.SerializeToString(&serialized_sharing_message);
incoming_message.raw_data = serialized_sharing_message;
return incoming_message;
}
void SendTextMessage(const std::string& device_name,
const std::string& text) {
sharing_service_->GetFCMHandlerForTesting()->OnMessage(
kSharingFCMAppID,
CreateMessage(device_name, text, /*image_url=*/std::nullopt));
}
void SendImageMessage(const std::string& device_name, const GURL& image_url) {
base::RunLoop run_loop;
ClipboardObserver observer(run_loop.QuitClosure());
ui::ClipboardMonitor::GetInstance()->AddObserver(&observer);
sharing_service_->GetFCMHandlerForTesting()->OnMessage(
kSharingFCMAppID,
CreateMessage(device_name, /*text*/ std::nullopt, image_url));
run_loop.Run();
ui::ClipboardMonitor::GetInstance()->RemoveObserver(&observer);
}
std::vector<std::u16string> GetAvailableClipboardTypes() {
std::vector<std::u16string> types;
ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes(
ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, &types);
return types;
}
std::string ReadClipboardText() {
std::u16string text;
ui::Clipboard::GetForCurrentThread()->ReadText(
ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, &text);
return base::UTF16ToUTF8(text);
}
SkBitmap ReadClipboardImage() {
std::vector<uint8_t> png_data =
ui::clipboard_test_util::ReadPng(ui::Clipboard::GetForCurrentThread());
SkBitmap bitmap = gfx::PNGCodec::Decode(png_data);
CHECK(!bitmap.isNull());
return bitmap;
}
message_center::Notification GetNotification() {
auto notifications = notification_tester_->GetDisplayedNotificationsForType(
NotificationHandler::Type::SHARING);
EXPECT_EQ(notifications.size(), 1u);
return notifications[0];
}
protected:
base::HistogramTester histograms_;
std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
raw_ptr<SharingService, DanglingUntriaged> sharing_service_;
std::unique_ptr<net::EmbeddedTestServer> server_;
};
IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, Text) {
// The clipboard is empty.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
// Send a message with text.
SendTextMessage(kDeviceName, kText);
// The text is in the clipboard and a notification is shown.
std::vector<std::u16string> types = GetAvailableClipboardTypes();
size_t expected_size = 1u;
ASSERT_EQ(expected_size, types.size());
ASSERT_EQ(ui::kMimeTypePlainText, base::UTF16ToASCII(types[0]));
if (expected_size == 2u) {
ASSERT_EQ(ui::kMimeTypeUtf8PlainText, base::UTF16ToASCII(types[1]));
}
ASSERT_EQ(kText, ReadClipboardText());
message_center::Notification notification = GetNotification();
ASSERT_EQ(l10n_util::GetStringFUTF16(
IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_TEXT_CONTENT,
kDeviceName16),
notification.title());
ASSERT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
}
IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, ImageUrl) {
// The clipboard is empty.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
// Send a message with an image url.
SendImageMessage(kDeviceName, server_->GetURL("/image_decoding/droids.jpg"));
// The image is in the clipboard and a notification is shown.
std::vector<std::u16string> types = GetAvailableClipboardTypes();
ASSERT_EQ(1u, types.size());
ASSERT_EQ(ui::kMimeTypePng, base::UTF16ToASCII(types[0]));
SkBitmap bitmap = ReadClipboardImage();
ASSERT_FALSE(bitmap.drawsNothing());
ASSERT_EQ(2560, bitmap.width());
ASSERT_EQ(1920, bitmap.height());
message_center::Notification notification = GetNotification();
ASSERT_EQ(l10n_util::GetStringFUTF16(
IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_IMAGE_CONTENT,
kDeviceName16),
notification.title());
ASSERT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
}
IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, TextThenImageUrl) {
// The clipboard is empty.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
// Send a message with text.
SendTextMessage(kDeviceName, kText);
// The text is in the clipboard.
std::vector<std::u16string> types = GetAvailableClipboardTypes();
size_t expected_size = 1u;
ASSERT_EQ(expected_size, types.size());
ASSERT_EQ(ui::kMimeTypePlainText, base::UTF16ToASCII(types[0]));
if (expected_size == 2u) {
ASSERT_EQ(ui::kMimeTypeUtf8PlainText, base::UTF16ToASCII(types[1]));
}
ASSERT_EQ(kText, ReadClipboardText());
// Send a message with an image url.
SendImageMessage(kDeviceName, server_->GetURL("/image_decoding/droids.jpg"));
// The image is in the clipboard and the text has been cleared.
types = GetAvailableClipboardTypes();
ASSERT_EQ(1u, types.size());
ASSERT_EQ(ui::kMimeTypePng, base::UTF16ToASCII(types[0]));
ASSERT_EQ(std::string(), ReadClipboardText());
}