blob: d8291d2e86f1f9bd94078e0d0618ec3e31ba13be [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <string>
#include <vector>
#include "base/guid.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
#include "chrome/browser/sharing/shared_clipboard/remote_copy_handle_message_result.h"
#include "chrome/browser/sharing/sharing_constants.h"
#include "chrome/browser/sharing/sharing_fcm_handler.h"
#include "chrome/browser/sharing/sharing_service.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 "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/test_clipboard.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/public/cpp/notification.h"
namespace {
const char kDeviceName[] = "test device name";
const char kText[] = "test text";
const char kResultHistogram[] = "Sharing.RemoteCopyHandleMessageResult";
const char kTextSizeHistogram[] = "Sharing.RemoteCopyReceivedTextSize";
const char kImageSizeBeforeDecodeHistogram[] =
"Sharing.RemoteCopyReceivedImageSizeBeforeDecode";
const char kImageSizeAfterDecodeHistogram[] =
"Sharing.RemoteCopyReceivedImageSizeAfterDecode";
const char kStatusCodeHistogram[] = "Sharing.RemoteCopyLoadImageStatusCode";
const char kLoadTimeHistogram[] = "Sharing.RemoteCopyLoadImageTime";
const char kDecodeTimeHistogram[] = "Sharing.RemoteCopyDecodeImageTime";
const char kResizeTimeHistogram[] = "Sharing.RemoteCopyResizeImageTime";
class ClipboardObserver : public ui::ClipboardObserver {
public:
explicit ClipboardObserver(base::RepeatingClosure callback)
: callback_(callback) {}
void OnClipboardDataChanged() override { callback_.Run(); }
private:
base::RepeatingClosure callback_;
DISALLOW_COPY_AND_ASSIGN(ClipboardObserver);
};
} // namespace
// Browser tests for the Remote Copy feature.
class RemoteCopyBrowserTestBase : public InProcessBrowserTest {
public:
RemoteCopyBrowserTestBase() = default;
~RemoteCopyBrowserTestBase() override = default;
void SetUpOnMainThread() override {
ui::TestClipboard::CreateForCurrentThread();
notification_tester_ = std::make_unique<NotificationDisplayServiceTester>(
browser()->profile());
sharing_service_ =
SharingServiceFactory::GetForBrowserContext(browser()->profile());
}
void TearDownOnMainThread() override {
notification_tester_.reset();
ui::Clipboard::DestroyClipboardForCurrentThread();
}
gcm::IncomingMessage CreateMessage(const std::string& device_name,
base::Optional<std::string> text,
base::Optional<GURL> image_url) {
chrome_browser_sharing::SharingMessage sharing_message;
sharing_message.set_sender_guid(base::GenerateGUID());
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=*/base::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*/ base::nullopt, image_url));
run_loop.Run();
ui::ClipboardMonitor::GetInstance()->RemoveObserver(&observer);
}
std::vector<base::string16> GetAvailableClipboardTypes() {
std::vector<base::string16> types;
bool contains_filenames;
ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes(
ui::ClipboardBuffer::kCopyPaste, &types, &contains_filenames);
return types;
}
std::string ReadClipboardText() {
base::string16 text;
ui::Clipboard::GetForCurrentThread()->ReadText(
ui::ClipboardBuffer::kCopyPaste, &text);
return base::UTF16ToUTF8(text);
}
SkBitmap ReadClipboardImage() {
return ui::Clipboard::GetForCurrentThread()->ReadImage(
ui::ClipboardBuffer::kCopyPaste);
}
message_center::Notification GetNotification() {
auto notifications = notification_tester_->GetDisplayedNotificationsForType(
NotificationHandler::Type::SHARING);
EXPECT_EQ(notifications.size(), 1u);
return notifications[0];
}
protected:
base::test::ScopedFeatureList feature_list_;
base::HistogramTester histograms_;
std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
SharingService* sharing_service_;
std::unique_ptr<net::EmbeddedTestServer> server_;
private:
DISALLOW_COPY_AND_ASSIGN(RemoteCopyBrowserTestBase);
};
class RemoteCopyDisabledBrowserTest : public RemoteCopyBrowserTestBase {
public:
RemoteCopyDisabledBrowserTest() {
feature_list_.InitAndDisableFeature(kRemoteCopyReceiver);
}
};
IN_PROC_BROWSER_TEST_F(RemoteCopyDisabledBrowserTest, FeatureDisabled) {
// The clipboard is empty.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
// Send a message with text.
SendTextMessage(kDeviceName, kText);
// The clipboard is still empty because the feature is disabled and the
// handler is not installed.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
histograms_.ExpectTotalCount(kResultHistogram, 0);
}
class RemoteCopyBrowserTest : public RemoteCopyBrowserTestBase {
public:
RemoteCopyBrowserTest() {
server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTP);
server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
EXPECT_TRUE(server_->Start());
url::Origin allowlist_origin = url::Origin::Create(server_->base_url());
feature_list_.InitWithFeaturesAndParameters(
{{kRemoteCopyReceiver,
{{kRemoteCopyAllowedOrigins.name, allowlist_origin.Serialize()}}},
{kRemoteCopyImageNotification, {}}},
{});
}
};
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<base::string16> types = GetAvailableClipboardTypes();
ASSERT_EQ(1u, types.size());
ASSERT_EQ(ui::kMimeTypeText, base::UTF16ToASCII(types[0]));
ASSERT_EQ(kText, ReadClipboardText());
message_center::Notification notification = GetNotification();
ASSERT_EQ(l10n_util::GetStringFUTF16(
IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_TEXT_CONTENT,
base::ASCIIToUTF16(kDeviceName)),
notification.title());
ASSERT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
histograms_.ExpectUniqueSample(
kResultHistogram, RemoteCopyHandleMessageResult::kSuccessHandledText, 1);
histograms_.ExpectUniqueSample(kTextSizeHistogram, std::string(kText).size(),
1);
}
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<base::string16> 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,
base::ASCIIToUTF16(kDeviceName)),
notification.title());
ASSERT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification.type());
ASSERT_EQ(640, notification.rich_notification_data().image.Width());
ASSERT_EQ(480, notification.rich_notification_data().image.Height());
histograms_.ExpectUniqueSample(kStatusCodeHistogram, net::HTTP_OK, 1);
histograms_.ExpectTotalCount(kLoadTimeHistogram, 1);
histograms_.ExpectUniqueSample(kImageSizeBeforeDecodeHistogram, 810490, 1);
histograms_.ExpectTotalCount(kDecodeTimeHistogram, 1);
histograms_.ExpectUniqueSample(kImageSizeAfterDecodeHistogram, 19660800, 1);
histograms_.ExpectTotalCount(kResizeTimeHistogram, 1);
histograms_.ExpectUniqueSample(
kResultHistogram, RemoteCopyHandleMessageResult::kSuccessHandledImage, 1);
}
IN_PROC_BROWSER_TEST_F(RemoteCopyBrowserTest, TextThenImageUrl) {
// The clipboard is empty.
ASSERT_TRUE(GetAvailableClipboardTypes().empty());
histograms_.ExpectTotalCount(kResultHistogram, 0);
// Send a message with text.
SendTextMessage(kDeviceName, kText);
// The text is in the clipboard.
std::vector<base::string16> types = GetAvailableClipboardTypes();
ASSERT_EQ(1u, types.size());
ASSERT_EQ(ui::kMimeTypeText, base::UTF16ToASCII(types[0]));
ASSERT_EQ(kText, ReadClipboardText());
histograms_.ExpectTotalCount(kResultHistogram, 1);
histograms_.ExpectUniqueSample(
kResultHistogram, RemoteCopyHandleMessageResult::kSuccessHandledText, 1);
// 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());
histograms_.ExpectTotalCount(kResultHistogram, 2);
histograms_.ExpectBucketCount(
kResultHistogram, RemoteCopyHandleMessageResult::kSuccessHandledImage, 1);
}