Commit ba05b80c authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Support text/uri-list for file drag and drop in exo

Bug: 1144138

Change-Id: I57414924c9b09d100af6b6fa2b1f713310104209
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2504447
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#824178}
parent ad5753fd
......@@ -13,6 +13,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/pickle.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
......@@ -20,6 +21,7 @@
#include "components/exo/data_offer_delegate.h"
#include "components/exo/data_offer_observer.h"
#include "components/exo/file_helper.h"
#include "net/base/filename_util.h"
#include "third_party/skia/include/core/SkEncodedImageFormat.h"
#include "third_party/skia/include/core/SkImageEncoder.h"
#include "third_party/skia/include/core/SkStream.h"
......@@ -38,6 +40,7 @@ constexpr char kTextMimeTypeUtf16[] = "text/plain;charset=utf-16";
constexpr char kTextHtmlMimeTypeUtf8[] = "text/html;charset=utf-8";
constexpr char kTextHtmlMimeTypeUtf16[] = "text/html;charset=utf-16";
constexpr char kTextRtfMimeType[] = "text/rtf";
constexpr char kTextUriListMimeType[] = "text/uri-list";
constexpr char kImagePngMimeType[] = "image/png";
constexpr char kUriListSeparator[] = "\r\n";
......@@ -263,6 +266,22 @@ void DataOffer::SetDropData(FileHelper* file_helper,
const ui::OSExchangeData& data) {
DCHECK_EQ(0u, data_callbacks_.size());
std::string filenames_content;
// TODO(crbug.com/1144138): If we are dropping this in a VM, we must
// translate paths, and share paths at the time when data is received.
if (data.HasFile()) {
std::vector<ui::FileInfo> files;
std::vector<std::string> lines;
data.GetFilenames(&files);
for (const auto& file : files)
lines.emplace_back(net::FilePathToFileURL(file.path).spec());
filenames_content = base::JoinString(lines, kUriListSeparator);
data_callbacks_.emplace(
kTextUriListMimeType,
AsyncSend(base::RefCountedString::TakeString(&filenames_content)));
delegate_->OnOffer(kTextUriListMimeType);
}
const std::string uri_list_mime_type = file_helper->GetMimeTypeForUriList();
base::string16 url_list_string;
if (GetUrlListFromDataFile(file_helper, data, &url_list_string)) {
......
......@@ -308,9 +308,9 @@ TEST_F(DataOfferTest, ReceiveUriList) {
ASSERT_TRUE(base::CreatePipe(&read_pipe, &write_pipe));
data_offer.Receive("text/uri-list", std::move(write_pipe));
base::string16 result;
ASSERT_TRUE(ReadString16(std::move(read_pipe), &result));
EXPECT_EQ(base::ASCIIToUTF16("file:///test/downloads/file"), result);
std::string result;
ASSERT_TRUE(ReadString(std::move(read_pipe), &result));
EXPECT_EQ("file:///test/downloads/file", result);
}
TEST_F(DataOfferTest, ReceiveUriListFromPickle_ReceiveBeforeUrlIsResolved) {
......
......@@ -31,6 +31,7 @@ namespace {
constexpr char kTextPlain[] = "text/plain";
constexpr char kTextRTF[] = "text/rtf";
constexpr char kTextHTML[] = "text/html";
constexpr char kTextUriList[] = "text/uri-list";
constexpr char kUtfPrefix[] = "UTF";
constexpr char kEncoding16[] = "16";
......@@ -240,8 +241,9 @@ void DataSource::GetDataForPreferredMimeTypes(
ReadDataCallback rtf_reader,
ReadTextDataCallback html_reader,
ReadDataCallback image_reader,
ReadDataCallback filenames_reader,
base::RepeatingClosure failure_callback) {
std::string text_mime, rtf_mime, html_mime, image_mime;
std::string text_mime, rtf_mime, html_mime, image_mime, filenames_mime;
int text_rank = std::numeric_limits<int>::max();
int html_rank = std::numeric_limits<int>::max();
......@@ -286,6 +288,11 @@ void DataSource::GetDataForPreferredMimeTypes(
image_mime = mime_type;
image_rank = new_rank;
}
} else if (net::MatchesMimeType(std::string(kTextUriList), mime_type)) {
if (filenames_reader.is_null())
continue;
filenames_mime = mime_type;
}
}
......@@ -301,6 +308,7 @@ void DataSource::GetDataForPreferredMimeTypes(
std::move(html_reader)),
failure_callback);
ReadData(image_mime, std::move(image_reader), failure_callback);
ReadData(filenames_mime, std::move(filenames_reader), failure_callback);
}
void DataSource::OnTextRead(ReadTextDataCallback callback,
......
......@@ -60,8 +60,9 @@ class DataSource {
void DndFinished();
// Search the set of offered MIME types for the most preferred of each of the
// following categories: text/plain*, text/rtf, text/html*, image/*. If any
// usable MIME types in a given category are available, the corresponding
// following categories: text/plain*, text/rtf, text/html*, image/*,
// text/uri-list. If any usable MIME types in a given category are available,
// the corresponding
// |*_reader| input callback will be called with the best one and the
// corresponding data. For any category that has no available MIME types,
// |failure_callback| is run. |failure_callback| may therefore be run as many
......@@ -74,6 +75,7 @@ class DataSource {
ReadDataCallback rtf_reader,
ReadTextDataCallback html_reader,
ReadDataCallback image_reader,
ReadDataCallback filenames_reader,
base::RepeatingClosure failure_callback);
void ReadDataForTesting(const std::string& mime_type,
......
......@@ -68,11 +68,12 @@ void IncrementCounter(base::RepeatingClosure counter) {
std::move(counter).Run();
}
void CheckMimeTypesRecieved(DataSource* data_source,
void CheckMimeTypesReceived(DataSource* data_source,
const std::string& text_mime,
const std::string& rtf_mime,
const std::string& html_mime,
const std::string& image_mime) {
const std::string& image_mime,
const std::string& filenames_mime) {
base::RunLoop run_loop;
base::RepeatingClosure counter =
base::BarrierClosure(4, run_loop.QuitClosure());
......@@ -81,6 +82,7 @@ void CheckMimeTypesRecieved(DataSource* data_source,
base::BindOnce(&CheckMimeType, rtf_mime, counter),
base::BindOnce(&CheckTextMimeType, html_mime, counter),
base::BindOnce(&CheckMimeType, image_mime, counter),
base::BindOnce(&CheckMimeType, filenames_mime, counter),
base::BindRepeating(&IncrementCounter, counter));
run_loop.Run();
}
......@@ -170,11 +172,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeUTF16) {
data_source.Offer("text/html;charset=UTF-16");
data_source.Offer("text/html;charset=utf-8");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=utf-16",
"",
"text/html;charset=UTF-16",
"",
"");
}
......@@ -186,11 +189,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeUTF16LE) {
data_source.Offer("text/html;charset=utf16le");
data_source.Offer("text/html;charset=utf-8");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=utf-16le",
"",
"text/html;charset=utf16le",
"",
"");
}
......@@ -202,11 +206,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeUTF16BE) {
data_source.Offer("text/html;charset=UTF16be");
data_source.Offer("text/html;charset=utf-8");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=utf-16be",
"",
"text/html;charset=UTF16be",
"",
"");
}
......@@ -218,11 +223,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeUTFToOther) {
data_source.Offer("text/html;charset=utf-8");
data_source.Offer("text/html;charset=iso-8859-1");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=utf-8",
"",
"text/html;charset=utf-8",
"",
"");
}
......@@ -232,11 +238,12 @@ TEST_F(DataSourceTest, RecogniseUTF8Legaccy) {
data_source.Offer("UTF8_STRING");
data_source.Offer("text/plain;charset=iso-8859-1");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"UTF8_STRING",
"",
"",
"",
"");
}
......@@ -248,11 +255,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeOtherToAscii) {
data_source.Offer("text/html;charset=iso-8859-1");
data_source.Offer("text/html;charset=ascii");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=iso-8859-1",
"",
"text/html;charset=iso-8859-1",
"",
"");
}
......@@ -264,11 +272,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeOtherToUnspecified) {
data_source.Offer("text/html;charset=iso-8859-1");
data_source.Offer("text/html");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"text/plain;charset=iso-8859-1",
"",
"text/html;charset=iso-8859-1",
"",
"");
}
......@@ -277,11 +286,12 @@ TEST_F(DataSourceTest, PreferredMimeTypeRTF) {
DataSource data_source(&delegate);
data_source.Offer("text/rtf");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"",
"text/rtf",
"",
"",
"");
}
......@@ -291,12 +301,13 @@ TEST_F(DataSourceTest, PreferredMimeTypeBitmapToPNG) {
data_source.Offer("image/bmp");
data_source.Offer("image/png");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"",
"",
"",
"image/bmp");
"image/bmp",
"");
}
TEST_F(DataSourceTest, PreferredMimeTypePNGToJPEG) {
......@@ -306,12 +317,27 @@ TEST_F(DataSourceTest, PreferredMimeTypePNGToJPEG) {
data_source.Offer("image/jpeg");
data_source.Offer("image/jpg");
CheckMimeTypesRecieved(
CheckMimeTypesReceived(
&data_source,
"",
"",
"",
"image/png",
"");
}
TEST_F(DataSourceTest, PreferredMimeTypeTextUriList) {
TestDataSourceDelegate delegate;
DataSource data_source(&delegate);
data_source.Offer("text/uri-list");
CheckMimeTypesReceived(
&data_source,
"",
"",
"",
"image/png");
"",
"text/uri-list");
}
} // namespace
......
......@@ -6,6 +6,7 @@
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/strings/string_split.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/exo/data_offer.h"
#include "components/exo/data_source.h"
......@@ -16,6 +17,7 @@
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/file_info/file_info.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
......@@ -23,6 +25,7 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/transform_util.h"
#include "url/gurl.h"
#if defined(OS_CHROMEOS)
#include "ash/drag_drop/drag_drop_controller.h"
......@@ -146,7 +149,10 @@ DragDropOperation::DragDropOperation(DataSource* source,
DataSource::ReadDataCallback(),
base::BindOnce(&DragDropOperation::OnHTMLRead,
weak_ptr_factory_.GetWeakPtr()),
DataSource::ReadDataCallback(), counter_);
DataSource::ReadDataCallback(),
base::BindOnce(&DragDropOperation::OnFilenamesRead,
weak_ptr_factory_.GetWeakPtr()),
counter_);
if (icon) {
origin_->get()->window()->AddChild(host_window());
......@@ -193,6 +199,28 @@ void DragDropOperation::OnHTMLRead(const std::string& mime_type,
counter_.Run();
}
void DragDropOperation::OnFilenamesRead(const std::string& mime_type,
const std::vector<uint8_t>& data) {
DCHECK(os_exchange_data_);
std::string lines(data.begin(), data.end());
std::vector<ui::FileInfo> filenames;
for (const base::StringPiece& line : base::SplitStringPiece(
lines, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
if (line[0] == '#')
continue;
GURL url(line);
// TODO(crbug.com/1144138): We must translate the path if this was received
// from a VM. E.g. if this was from crostini as
// file:///home/username/file.txt, we translate to
// file:///media/fuse/crostini_<hash>_termina_penguin/file.txt.
filenames.emplace_back(
ui::FileInfo(base::FilePath(url.path()), base::FilePath()));
}
os_exchange_data_->SetFilenames(std::move(filenames));
mime_type_ = mime_type;
counter_.Run();
}
void DragDropOperation::OnSurfaceCommit() {
SurfaceTreeHost::OnSurfaceCommit();
......
......@@ -96,6 +96,8 @@ class DragDropOperation : public DataSourceObserver,
void OnTextRead(const std::string& mime_type, base::string16 data);
void OnHTMLRead(const std::string& mime_type, base::string16 data);
void OnFilenamesRead(const std::string& mime_type,
const std::vector<uint8_t>& data);
void ScheduleStartDragDropOperation();
......
......@@ -153,6 +153,8 @@ void Seat::SetSelection(DataSource* source) {
data_read_callback),
base::BindOnce(&Seat::OnImageRead, weak_ptr_factory_.GetWeakPtr(), writer,
data_read_callback),
base::BindOnce(&Seat::OnFilenamesRead, weak_ptr_factory_.GetWeakPtr(),
writer, data_read_callback),
data_read_callback);
}
......@@ -220,6 +222,14 @@ void Seat::OnImageDecoded(base::OnceClosure callback,
}
#endif // defined(OS_CHROMEOS)
void Seat::OnFilenamesRead(
scoped_refptr<RefCountedScopedClipboardWriter> writer,
base::OnceClosure callback,
const std::string& mime_type,
const std::vector<uint8_t>& data) {
std::move(callback).Run();
}
void Seat::OnAllReadsFinished(
scoped_refptr<RefCountedScopedClipboardWriter> writer) {
// We need to destroy the ScopedClipboardWriter in this call, before
......
......@@ -38,8 +38,8 @@ class Surface;
class XkbTracker;
// The maximum number of different data types that we will write to the
// clipboard (plain text, RTF, HTML, image)
constexpr int kMaxClipboardDataTypes = 4;
// clipboard (plain text, RTF, HTML, image, text/uri-list)
constexpr int kMaxClipboardDataTypes = 5;
// Seat object represent a group of input devices such as keyboard, pointer and
// touch devices and keeps track of input focus.
......@@ -158,6 +158,10 @@ class Seat : public aura::client::FocusChangeObserver,
scoped_refptr<RefCountedScopedClipboardWriter> writer,
const SkBitmap& bitmap);
#endif // defined(OS_CHROMEOS)
void OnFilenamesRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
base::OnceClosure callback,
const std::string& mime_type,
const std::vector<uint8_t>& data);
void OnAllReadsFinished(
scoped_refptr<RefCountedScopedClipboardWriter> writer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment