Commit 2c1abe82 authored by Miyoung Shin's avatar Miyoung Shin Committed by Commit Bot

Fix multiple file paths issue for dragging

This CL reimplements to use one CF_HDROP clipboard format having
series of file path's strings instead of series of CF_HDROP having
a file path string to get multiple file paths correctly for
dragging.

BUG=878690

Change-Id: I167e93f6411f866cb143b910b1fc7d4896481ffc
Reviewed-on: https://chromium-review.googlesource.com/1195176Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Miyoung Shin <myid.shin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595759}
parent 859079e8
...@@ -26,15 +26,6 @@ ...@@ -26,15 +26,6 @@
#include "ui/wm/core/default_screen_position_client.h" #include "ui/wm/core/default_screen_position_client.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace ui {
// An equal-to operator to make EXPECT_EQ happy.
bool operator==(const FileInfo& info1, const FileInfo& info2) {
return info1.path == info2.path && info1.display_name == info2.display_name;
}
} // namespace ui
namespace ws { namespace ws {
class DragDropDelegateTest : public testing::Test { class DragDropDelegateTest : public testing::Test {
......
...@@ -14,4 +14,8 @@ FileInfo::FileInfo(const base::FilePath& path, ...@@ -14,4 +14,8 @@ FileInfo::FileInfo(const base::FilePath& path,
FileInfo::~FileInfo() {} FileInfo::~FileInfo() {}
bool FileInfo::operator==(const FileInfo& other) const {
return path == other.path && display_name == other.display_name;
}
} // namespace ui } // namespace ui
...@@ -15,6 +15,7 @@ struct UI_BASE_EXPORT FileInfo { ...@@ -15,6 +15,7 @@ struct UI_BASE_EXPORT FileInfo {
FileInfo(); FileInfo();
FileInfo(const base::FilePath& path, const base::FilePath& display_name); FileInfo(const base::FilePath& path, const base::FilePath& display_name);
~FileInfo(); ~FileInfo();
bool operator==(const FileInfo& other) const;
base::FilePath path; base::FilePath path;
base::FilePath display_name; // Optional. base::FilePath display_name; // Optional.
......
...@@ -57,8 +57,9 @@ static void GetInternetShortcutFileContents(const GURL& url, std::string* data); ...@@ -57,8 +57,9 @@ static void GetInternetShortcutFileContents(const GURL& url, std::string* data);
static void CreateValidFileNameFromTitle(const GURL& url, static void CreateValidFileNameFromTitle(const GURL& url,
const base::string16& title, const base::string16& title,
base::string16* validated); base::string16* validated);
// Creates a new STGMEDIUM object to hold a file. // Creates a new STGMEDIUM object to hold files.
static STGMEDIUM* GetStorageForFileName(const base::FilePath& path); static STGMEDIUM* GetStorageForFileNames(
const std::vector<FileInfo>& filenames);
static STGMEDIUM* GetIDListStorageForFileName(const base::FilePath& path); static STGMEDIUM* GetIDListStorageForFileName(const base::FilePath& path);
// Creates a File Descriptor for the creation of a file to the given URL and // Creates a File Descriptor for the creation of a file to the given URL and
// returns a handle to it. // returns a handle to it.
...@@ -346,7 +347,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, ...@@ -346,7 +347,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url,
Clipboard::GetUrlFormatType().ToFormatEtc(), storage)); Clipboard::GetUrlFormatType().ToFormatEtc(), storage));
// TODO(beng): add CF_HTML. // TODO(beng): add CF_HTML.
// http://code.google.com/p/chromium/issues/detail?id=6767 // http://code.google.com/p/chromium/issues/detail?id=6767GetIDListStorageForFileName
// Also add text representations (these should be last since they're the // Also add text representations (these should be last since they're the
// least preferable). // least preferable).
...@@ -354,11 +355,9 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, ...@@ -354,11 +355,9 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url,
} }
void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) { void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) {
STGMEDIUM* storage = GetStorageForFileName(path); SetFilenames({FileInfo(path, base::FilePath())});
data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>(
Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage));
storage = GetIDListStorageForFileName(path); STGMEDIUM* storage = GetIDListStorageForFileName(path);
if (!storage) if (!storage)
return; return;
data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>(
...@@ -367,11 +366,12 @@ void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) { ...@@ -367,11 +366,12 @@ void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) {
void OSExchangeDataProviderWin::SetFilenames( void OSExchangeDataProviderWin::SetFilenames(
const std::vector<FileInfo>& filenames) { const std::vector<FileInfo>& filenames) {
for (size_t i = 0; i < filenames.size(); ++i) { STGMEDIUM* storage = GetStorageForFileNames(filenames);
STGMEDIUM* storage = GetStorageForFileName(filenames[i].path); if (!storage)
data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( return;
Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage));
} data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>(
Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage));
} }
void OSExchangeDataProviderWin::SetPickledData( void OSExchangeDataProviderWin::SetPickledData(
...@@ -537,7 +537,7 @@ void OSExchangeDataProviderWin::SetDownloadFileInfo( ...@@ -537,7 +537,7 @@ void OSExchangeDataProviderWin::SetDownloadFileInfo(
// think we always synthesize one in WebContentsDragWin. // think we always synthesize one in WebContentsDragWin.
STGMEDIUM* storage = NULL; STGMEDIUM* storage = NULL;
if (!download.filename.empty()) if (!download.filename.empty())
storage = GetStorageForFileName(download.filename); GetStorageForFileNames({FileInfo(download.filename, base::FilePath())});
// Add CF_HDROP. // Add CF_HDROP.
auto info = std::make_unique<DataObjectImpl::StoredDataInfo>( auto info = std::make_unique<DataObjectImpl::StoredDataInfo>(
...@@ -752,8 +752,12 @@ void DataObjectImpl::OnDownloadCompleted(const base::FilePath& file_path) { ...@@ -752,8 +752,12 @@ void DataObjectImpl::OnDownloadCompleted(const base::FilePath& file_path) {
} }
// Update the storage. // Update the storage.
(*iter)->owns_medium = true; STGMEDIUM* storage =
(*iter)->medium = GetStorageForFileName(file_path); GetStorageForFileNames({FileInfo(file_path, base::FilePath())});
if (storage) {
(*iter)->owns_medium = true;
(*iter)->medium = storage;
}
break; break;
} }
...@@ -1003,26 +1007,52 @@ static void CreateValidFileNameFromTitle(const GURL& url, ...@@ -1003,26 +1007,52 @@ static void CreateValidFileNameFromTitle(const GURL& url,
*validated += extension; *validated += extension;
} }
static STGMEDIUM* GetStorageForFileName(const base::FilePath& path) { static STGMEDIUM* GetStorageForFileNames(
const size_t kDropSize = sizeof(DROPFILES); const std::vector<FileInfo>& filenames) {
const size_t kTotalBytes = // CF_HDROP clipboard format consists of DROPFILES structure, a series of file
kDropSize + (path.value().length() + 2) * sizeof(wchar_t); // names including the terminating null character and the additional null
HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes); // character at the tail to terminate the array.
// For example,
//| DROPFILES | FILENAME 1 | NULL | ... | FILENAME n | NULL | NULL |
// For more details, please refer to
// https://docs.microsoft.com/ko-kr/windows/desktop/shell/clipboard#cf_hdrop
if (filenames.empty())
return nullptr;
const size_t kDropFilesHeaderSizeInBytes = sizeof(DROPFILES);
size_t total_bytes = kDropFilesHeaderSizeInBytes;
for (const auto& filename : filenames) {
// Allocate memory of the filename's length including the null
// character.
total_bytes += (filename.path.value().length() + 1) * sizeof(wchar_t);
}
// |data| needs to be terminated by an additional null character.
total_bytes += sizeof(wchar_t);
// GHND combines GMEM_MOVEABLE and GMEM_ZEROINIT, and GMEM_ZEROINIT
// initializes memory contents to zero.
HANDLE hdata = GlobalAlloc(GHND, total_bytes);
base::win::ScopedHGlobal<DROPFILES*> locked_mem(hdata); base::win::ScopedHGlobal<DROPFILES*> locked_mem(hdata);
DROPFILES* drop_files = locked_mem.get(); DROPFILES* drop_files = locked_mem.get();
drop_files->pFiles = sizeof(DROPFILES); drop_files->pFiles = sizeof(DROPFILES);
drop_files->fWide = TRUE; drop_files->fWide = TRUE;
wchar_t* data = reinterpret_cast<wchar_t*>( wchar_t* data = reinterpret_cast<wchar_t*>(
reinterpret_cast<BYTE*>(drop_files) + kDropSize); reinterpret_cast<BYTE*>(drop_files) + kDropFilesHeaderSizeInBytes);
const size_t copy_size = (path.value().length() + 1) * sizeof(wchar_t);
memcpy(data, path.value().c_str(), copy_size); size_t next_filename_offset = 0;
data[path.value().length() + 1] = L'\0'; // Double NULL for (const auto& filename : filenames) {
wcscpy(data + next_filename_offset, filename.path.value().c_str());
// Skip the terminating null character of the filename.
next_filename_offset += filename.path.value().length() + 1;
}
STGMEDIUM* storage = new STGMEDIUM; STGMEDIUM* storage = new STGMEDIUM;
storage->tymed = TYMED_HGLOBAL; storage->tymed = TYMED_HGLOBAL;
storage->hGlobal = hdata; storage->hGlobal = hdata;
storage->pUnkForRelease = NULL; storage->pUnkForRelease = nullptr;
return storage; return storage;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/win/scoped_hglobal.h" #include "base/win/scoped_hglobal.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard.h"
#include "ui/base/dragdrop/file_info.h"
#include "ui/base/dragdrop/os_exchange_data_provider_win.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -303,6 +304,23 @@ TEST(OSExchangeDataWinTest, FileContents) { ...@@ -303,6 +304,23 @@ TEST(OSExchangeDataWinTest, FileContents) {
EXPECT_EQ(file_contents, read_contents); EXPECT_EQ(file_contents, read_contents);
} }
TEST(OSExchangeDataWinTest, Filenames) {
OSExchangeData data;
const std::vector<FileInfo> kTestFilenames = {
{base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file1")),
base::FilePath()},
{base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file2")),
base::FilePath()},
};
data.SetFilenames(kTestFilenames);
OSExchangeData copy(data.provider().Clone());
std::vector<FileInfo> dropped_filenames;
EXPECT_TRUE(copy.GetFilenames(&dropped_filenames));
EXPECT_EQ(kTestFilenames, dropped_filenames);
}
TEST(OSExchangeDataWinTest, CFHtml) { TEST(OSExchangeDataWinTest, CFHtml) {
OSExchangeData data; OSExchangeData data;
GURL url("http://www.google.com/"); GURL url("http://www.google.com/");
......
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