blob: 61f79c7e4b759a2b326291f9b4fab40c39fe91f3 [file] [log] [blame]
// Copyright 2021 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 <string>
#include "base/base64.h"
#include "base/base_paths.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/clipboard/test/test_clipboard.h"
#include "ui/base/ui_base_features.h"
namespace content {
// End-to-end tests for clipboard file access.
class ClipboardHostImplBrowserTest : public ContentBrowserTest {
public:
struct File {
std::string name;
std::string type;
};
void SetUp() override {
ASSERT_TRUE(embedded_test_server()->Start());
features_.InitWithFeatures({features::kClipboardFilenames}, {});
ui::TestClipboard::CreateForCurrentThread();
ContentBrowserTest::SetUp();
}
void TearDown() override { ContentBrowserTest::TearDown(); }
void CopyPasteFiles(std::vector<File> files) {
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
// Create a promise that will resolve on paste with comma-separated
// '<name>:<type>:<b64-content>' of each file on the clipboard.
ASSERT_TRUE(
ExecJs(shell(),
"var p = new Promise((resolve, reject) => {"
" window.document.onpaste = async (event) => {"
" const data = event.clipboardData;"
" const files = [];"
" for (let i = 0; i < data.items.length; i++) {"
" if (data.items[i].kind != 'file') {"
" reject('The clipboard item[' + i +'] was of kind: ' +"
" data.items[i].kind + '. Expected file.');"
" }"
" files.push(data.files[i]);"
" }"
" const result = [];"
" for (let i = 0; i < files.length; i++) {"
" const file = files[i];"
" const buf = await file.arrayBuffer();"
" const buf8 = new Uint8Array(buf);"
" const b64 = btoa(String.fromCharCode(...buf8));"
" result.push(file.name + ':' + file.type + ':' + b64);"
" }"
" resolve(result.join(','));"
" };"
"});"));
// Put files on clipboard.
base::FilePath source_root;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root));
std::vector<std::string> expected;
std::vector<ui::FileInfo> file_infos;
{
base::ScopedAllowBlockingForTesting allow_blocking;
for (const auto& f : files) {
base::FilePath file =
source_root.AppendASCII("content/test/data/clipboard")
.AppendASCII(f.name);
std::string buf;
EXPECT_TRUE(base::ReadFileToString(file, &buf));
auto b64 = base::Base64Encode(base::as_bytes(base::make_span(buf)));
expected.push_back(base::JoinString({f.name, f.type, b64}, ":"));
file_infos.push_back(ui::FileInfo(file, base::FilePath()));
}
ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
writer.WriteFilenames(ui::FileInfosToURIList(file_infos));
}
// Send paste event and wait for JS promise to resolve with file data.
shell()->web_contents()->Paste();
EXPECT_EQ(base::JoinString(expected, ","), EvalJs(shell(), "p"));
}
protected:
base::test::ScopedFeatureList features_;
};
IN_PROC_BROWSER_TEST_F(ClipboardHostImplBrowserTest, TextFile) {
CopyPasteFiles({File{"hello.txt", "text/plain"}});
}
IN_PROC_BROWSER_TEST_F(ClipboardHostImplBrowserTest, ImageFile) {
CopyPasteFiles({File{"small.jpg", "image/jpeg"}});
}
IN_PROC_BROWSER_TEST_F(ClipboardHostImplBrowserTest, Empty) {
CopyPasteFiles({});
}
IN_PROC_BROWSER_TEST_F(ClipboardHostImplBrowserTest, Multiple) {
CopyPasteFiles({
File{"hello.txt", "text/plain"},
File{"small.jpg", "image/jpeg"},
});
}
} // namespace content