2014-07-01 00:30:29 +02:00
|
|
|
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
|
|
|
// reserved. Use of this source code is governed by a BSD-style license that
|
|
|
|
// can be found in the LICENSE file.
|
|
|
|
|
2016-11-18 00:52:42 +01:00
|
|
|
#include "tests/cefclient/browser/osr_dragdrop_win.h"
|
2014-07-01 00:30:29 +02:00
|
|
|
|
2014-10-27 22:34:48 +01:00
|
|
|
#if defined(CEF_USE_ATL)
|
|
|
|
|
2014-07-01 00:30:29 +02:00
|
|
|
#include <shellapi.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
#include <windowsx.h>
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
|
2014-07-11 22:10:05 +02:00
|
|
|
#include "include/wrapper/cef_helpers.h"
|
2016-11-18 00:52:42 +01:00
|
|
|
#include "tests/cefclient/browser/bytes_write_handler.h"
|
|
|
|
#include "tests/cefclient/browser/resource.h"
|
|
|
|
#include "tests/shared/browser/util_win.h"
|
2015-01-23 18:37:23 +01:00
|
|
|
|
|
|
|
namespace client {
|
2014-07-01 00:30:29 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
DWORD DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops) {
|
|
|
|
DWORD effect = DROPEFFECT_NONE;
|
|
|
|
if (allowed_ops & DRAG_OPERATION_COPY)
|
|
|
|
effect |= DROPEFFECT_COPY;
|
|
|
|
if (allowed_ops & DRAG_OPERATION_LINK)
|
|
|
|
effect |= DROPEFFECT_LINK;
|
|
|
|
if (allowed_ops & DRAG_OPERATION_MOVE)
|
|
|
|
effect |= DROPEFFECT_MOVE;
|
|
|
|
return effect;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRenderHandler::DragOperationsMask DropEffectToDragOperation(DWORD effect) {
|
|
|
|
DWORD operation = DRAG_OPERATION_NONE;
|
|
|
|
if (effect & DROPEFFECT_COPY)
|
|
|
|
operation |= DRAG_OPERATION_COPY;
|
|
|
|
if (effect & DROPEFFECT_LINK)
|
|
|
|
operation |= DRAG_OPERATION_LINK;
|
|
|
|
if (effect & DROPEFFECT_MOVE)
|
|
|
|
operation |= DRAG_OPERATION_MOVE;
|
|
|
|
return static_cast<CefRenderHandler::DragOperationsMask>(operation);
|
|
|
|
}
|
|
|
|
|
|
|
|
CefMouseEvent ToMouseEvent(POINTL p, DWORD key_state, HWND hWnd) {
|
|
|
|
CefMouseEvent ev;
|
2017-05-17 11:29:28 +02:00
|
|
|
POINT screen_point = {p.x, p.y};
|
2014-07-01 00:30:29 +02:00
|
|
|
ScreenToClient(hWnd, &screen_point);
|
|
|
|
ev.x = screen_point.x;
|
|
|
|
ev.y = screen_point.y;
|
2015-01-23 18:37:23 +01:00
|
|
|
ev.modifiers = GetCefMouseModifiers(key_state);
|
2014-07-01 00:30:29 +02:00
|
|
|
return ev;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetStorageForBytes(STGMEDIUM* storage, const void* data, size_t bytes) {
|
|
|
|
HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
|
|
|
|
if (handle) {
|
|
|
|
memcpy(handle, data, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
storage->hGlobal = handle;
|
|
|
|
storage->tymed = TYMED_HGLOBAL;
|
|
|
|
storage->pUnkForRelease = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void GetStorageForString(STGMEDIUM* stgmed, const std::basic_string<T>& data) {
|
2017-05-17 11:29:28 +02:00
|
|
|
GetStorageForBytes(
|
|
|
|
stgmed, data.c_str(),
|
2017-12-07 22:44:24 +01:00
|
|
|
(data.size() + 1) * sizeof(typename std::basic_string<T>::value_type));
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void GetStorageForFileDescriptor(STGMEDIUM* storage,
|
|
|
|
const std::wstring& file_name) {
|
2014-07-11 22:10:05 +02:00
|
|
|
DCHECK(!file_name.empty());
|
2014-07-01 00:30:29 +02:00
|
|
|
HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
|
|
|
|
|
|
|
|
FILEGROUPDESCRIPTOR* descriptor =
|
|
|
|
reinterpret_cast<FILEGROUPDESCRIPTOR*>(hdata);
|
|
|
|
descriptor->cItems = 1;
|
|
|
|
descriptor->fgd[0].dwFlags = FD_LINKUI;
|
|
|
|
wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(),
|
2017-05-17 11:29:28 +02:00
|
|
|
std::min(file_name.size(), static_cast<size_t>(MAX_PATH - 1u)));
|
2014-07-01 00:30:29 +02:00
|
|
|
|
|
|
|
storage->tymed = TYMED_HGLOBAL;
|
|
|
|
storage->hGlobal = hdata;
|
|
|
|
storage->pUnkForRelease = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper method for converting from text/html to MS CF_HTML.
|
|
|
|
// Documentation for the CF_HTML format is available at
|
|
|
|
// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
|
|
|
|
std::string HtmlToCFHtml(const std::string& html, const std::string& base_url) {
|
|
|
|
if (html.empty())
|
|
|
|
return std::string();
|
|
|
|
|
|
|
|
#define MAX_DIGITS 10
|
|
|
|
#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
|
|
|
|
#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
|
|
|
|
#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
static const char* header =
|
|
|
|
"Version:0.9\r\n"
|
|
|
|
"StartHTML:" NUMBER_FORMAT
|
|
|
|
"\r\n"
|
|
|
|
"EndHTML:" NUMBER_FORMAT
|
|
|
|
"\r\n"
|
|
|
|
"StartFragment:" NUMBER_FORMAT
|
|
|
|
"\r\n"
|
|
|
|
"EndFragment:" NUMBER_FORMAT "\r\n";
|
2014-07-01 00:30:29 +02:00
|
|
|
static const char* source_url_prefix = "SourceURL:";
|
|
|
|
|
|
|
|
static const char* start_markup = "<html>\r\n<body>\r\n<!--StartFragment-->";
|
|
|
|
static const char* end_markup = "<!--EndFragment-->\r\n</body>\r\n</html>";
|
|
|
|
|
|
|
|
// Calculate offsets
|
2017-05-17 11:29:28 +02:00
|
|
|
size_t start_html_offset =
|
|
|
|
strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
|
2014-07-01 00:30:29 +02:00
|
|
|
if (!base_url.empty()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
start_html_offset +=
|
|
|
|
strlen(source_url_prefix) + base_url.length() + 2; // Add 2 for \r\n.
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
size_t start_fragment_offset = start_html_offset + strlen(start_markup);
|
|
|
|
size_t end_fragment_offset = start_fragment_offset + html.length();
|
|
|
|
size_t end_html_offset = end_fragment_offset + strlen(end_markup);
|
|
|
|
char raw_result[1024];
|
2017-05-17 11:29:28 +02:00
|
|
|
_snprintf(raw_result, sizeof(1024), header, start_html_offset,
|
|
|
|
end_html_offset, start_fragment_offset, end_fragment_offset);
|
2014-07-01 00:30:29 +02:00
|
|
|
std::string result = raw_result;
|
|
|
|
if (!base_url.empty()) {
|
|
|
|
result.append(source_url_prefix);
|
|
|
|
result.append(base_url);
|
|
|
|
result.append("\r\n");
|
|
|
|
}
|
|
|
|
result.append(start_markup);
|
|
|
|
result.append(html);
|
|
|
|
result.append(end_markup);
|
|
|
|
|
|
|
|
#undef MAX_DIGITS
|
|
|
|
#undef MAKE_NUMBER_FORMAT_1
|
|
|
|
#undef MAKE_NUMBER_FORMAT_2
|
|
|
|
#undef NUMBER_FORMAT
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFHtmlExtractMetadata(const std::string& cf_html,
|
|
|
|
std::string* base_url,
|
|
|
|
size_t* html_start,
|
|
|
|
size_t* fragment_start,
|
|
|
|
size_t* fragment_end) {
|
|
|
|
// Obtain base_url if present.
|
|
|
|
if (base_url) {
|
|
|
|
static std::string src_url_str("SourceURL:");
|
|
|
|
size_t line_start = cf_html.find(src_url_str);
|
|
|
|
if (line_start != std::string::npos) {
|
|
|
|
size_t src_end = cf_html.find("\n", line_start);
|
|
|
|
size_t src_start = line_start + src_url_str.length();
|
|
|
|
if (src_end != std::string::npos && src_start != std::string::npos) {
|
|
|
|
*base_url = cf_html.substr(src_start, src_end - src_start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the markup between "<!--StartFragment-->" and "<!--EndFragment-->".
|
|
|
|
// If the comments cannot be found, like copying from OpenOffice Writer,
|
|
|
|
// we simply fall back to using StartFragment/EndFragment bytecount values
|
|
|
|
// to determine the fragment indexes.
|
|
|
|
std::string cf_html_lower = cf_html;
|
|
|
|
size_t markup_start = cf_html_lower.find("<html", 0);
|
|
|
|
if (html_start) {
|
|
|
|
*html_start = markup_start;
|
|
|
|
}
|
|
|
|
size_t tag_start = cf_html.find("<!--StartFragment", markup_start);
|
|
|
|
if (tag_start == std::string::npos) {
|
|
|
|
static std::string start_fragment_str("StartFragment:");
|
|
|
|
size_t start_fragment_start = cf_html.find(start_fragment_str);
|
|
|
|
if (start_fragment_start != std::string::npos) {
|
2017-05-17 11:29:28 +02:00
|
|
|
*fragment_start =
|
|
|
|
static_cast<size_t>(atoi(cf_html.c_str() + start_fragment_start +
|
|
|
|
start_fragment_str.length()));
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::string end_fragment_str("EndFragment:");
|
|
|
|
size_t end_fragment_start = cf_html.find(end_fragment_str);
|
|
|
|
if (end_fragment_start != std::string::npos) {
|
2017-05-17 11:29:28 +02:00
|
|
|
*fragment_end = static_cast<size_t>(atoi(
|
|
|
|
cf_html.c_str() + end_fragment_start + end_fragment_str.length()));
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*fragment_start = cf_html.find('>', tag_start) + 1;
|
|
|
|
size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
|
|
|
|
*fragment_end = cf_html.rfind('<', tag_end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFHtmlToHtml(const std::string& cf_html,
|
|
|
|
std::string* html,
|
|
|
|
std::string* base_url) {
|
|
|
|
size_t frag_start = std::string::npos;
|
|
|
|
size_t frag_end = std::string::npos;
|
|
|
|
|
|
|
|
CFHtmlExtractMetadata(cf_html, base_url, NULL, &frag_start, &frag_end);
|
|
|
|
|
|
|
|
if (html && frag_start != std::string::npos &&
|
|
|
|
frag_end != std::string::npos) {
|
|
|
|
*html = cf_html.substr(frag_start, frag_end - frag_start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const DWORD moz_url_format = ::RegisterClipboardFormat(L"text/x-moz-url");
|
|
|
|
const DWORD html_format = ::RegisterClipboardFormat(L"HTML Format");
|
2017-05-17 11:29:28 +02:00
|
|
|
const DWORD file_desc_format = ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
|
2014-07-01 00:30:29 +02:00
|
|
|
const DWORD file_contents_format =
|
|
|
|
::RegisterClipboardFormat(CFSTR_FILECONTENTS);
|
|
|
|
|
|
|
|
bool DragDataToDataObject(CefRefPtr<CefDragData> drag_data,
|
|
|
|
IDataObject** data_object) {
|
|
|
|
const int kMaxDataObjects = 10;
|
|
|
|
FORMATETC fmtetcs[kMaxDataObjects];
|
|
|
|
STGMEDIUM stgmeds[kMaxDataObjects];
|
2017-05-17 11:29:28 +02:00
|
|
|
FORMATETC fmtetc = {0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
2014-07-01 00:30:29 +02:00
|
|
|
int curr_index = 0;
|
|
|
|
CefString text = drag_data->GetFragmentText();
|
|
|
|
if (!text.empty()) {
|
|
|
|
fmtetc.cfFormat = CF_UNICODETEXT;
|
|
|
|
fmtetcs[curr_index] = fmtetc;
|
|
|
|
GetStorageForString(&stgmeds[curr_index], text.ToWString());
|
|
|
|
curr_index++;
|
|
|
|
}
|
|
|
|
if (drag_data->IsLink() && !drag_data->GetLinkURL().empty()) {
|
|
|
|
std::wstring x_moz_url_str = drag_data->GetLinkURL().ToWString();
|
|
|
|
x_moz_url_str += '\n';
|
|
|
|
x_moz_url_str += drag_data->GetLinkTitle().ToWString();
|
|
|
|
fmtetc.cfFormat = moz_url_format;
|
|
|
|
fmtetcs[curr_index] = fmtetc;
|
|
|
|
GetStorageForString(&stgmeds[curr_index], x_moz_url_str);
|
|
|
|
curr_index++;
|
|
|
|
}
|
|
|
|
CefString html = drag_data->GetFragmentHtml();
|
|
|
|
if (!html.empty()) {
|
|
|
|
CefString base_url = drag_data->GetFragmentBaseURL();
|
|
|
|
std::string cfhtml = HtmlToCFHtml(html.ToString(), base_url.ToString());
|
|
|
|
fmtetc.cfFormat = html_format;
|
|
|
|
fmtetcs[curr_index] = fmtetc;
|
|
|
|
GetStorageForString(&stgmeds[curr_index], cfhtml);
|
|
|
|
curr_index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t bufferSize = drag_data->GetFileContents(NULL);
|
|
|
|
if (bufferSize) {
|
|
|
|
CefRefPtr<BytesWriteHandler> handler = new BytesWriteHandler(bufferSize);
|
|
|
|
CefRefPtr<CefStreamWriter> writer =
|
|
|
|
CefStreamWriter::CreateForHandler(handler.get());
|
|
|
|
drag_data->GetFileContents(writer);
|
2014-07-11 22:10:05 +02:00
|
|
|
DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(bufferSize));
|
2014-07-01 00:30:29 +02:00
|
|
|
CefString fileName = drag_data->GetFileName();
|
|
|
|
GetStorageForFileDescriptor(&stgmeds[curr_index], fileName.ToWString());
|
|
|
|
fmtetc.cfFormat = file_desc_format;
|
|
|
|
fmtetcs[curr_index] = fmtetc;
|
|
|
|
curr_index++;
|
|
|
|
GetStorageForBytes(&stgmeds[curr_index], handler->GetData(),
|
2017-05-17 11:29:28 +02:00
|
|
|
handler->GetDataSize());
|
2014-07-01 00:30:29 +02:00
|
|
|
fmtetc.cfFormat = file_contents_format;
|
|
|
|
fmtetcs[curr_index] = fmtetc;
|
|
|
|
curr_index++;
|
|
|
|
}
|
2014-07-11 22:10:05 +02:00
|
|
|
DCHECK_LT(curr_index, kMaxDataObjects);
|
2014-07-01 00:30:29 +02:00
|
|
|
|
2017-12-07 22:44:24 +01:00
|
|
|
CComPtr<DataObjectWin> obj =
|
2014-07-01 00:30:29 +02:00
|
|
|
DataObjectWin::Create(fmtetcs, stgmeds, curr_index);
|
|
|
|
(*data_object) = obj.Detach();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDragData> DataObjectToDragData(IDataObject* data_object) {
|
|
|
|
CefRefPtr<CefDragData> drag_data = CefDragData::Create();
|
2020-01-15 15:28:12 +01:00
|
|
|
IEnumFORMATETC* enumFormats = nullptr;
|
2014-07-01 00:30:29 +02:00
|
|
|
HRESULT res = data_object->EnumFormatEtc(DATADIR_GET, &enumFormats);
|
|
|
|
if (res != S_OK)
|
|
|
|
return drag_data;
|
|
|
|
enumFormats->Reset();
|
|
|
|
const int kCelt = 10;
|
|
|
|
|
|
|
|
ULONG celtFetched;
|
|
|
|
do {
|
|
|
|
celtFetched = kCelt;
|
|
|
|
FORMATETC rgelt[kCelt];
|
|
|
|
res = enumFormats->Next(kCelt, rgelt, &celtFetched);
|
|
|
|
for (unsigned i = 0; i < celtFetched; i++) {
|
|
|
|
CLIPFORMAT format = rgelt[i].cfFormat;
|
2017-05-17 11:29:28 +02:00
|
|
|
if (!(format == CF_UNICODETEXT || format == CF_TEXT ||
|
|
|
|
format == moz_url_format || format == html_format ||
|
|
|
|
format == CF_HDROP) ||
|
|
|
|
rgelt[i].tymed != TYMED_HGLOBAL)
|
2014-07-01 00:30:29 +02:00
|
|
|
continue;
|
|
|
|
STGMEDIUM medium;
|
|
|
|
if (data_object->GetData(&rgelt[i], &medium) == S_OK) {
|
|
|
|
if (!medium.hGlobal) {
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
void* hGlobal = GlobalLock(medium.hGlobal);
|
|
|
|
if (!hGlobal) {
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (format == CF_UNICODETEXT) {
|
|
|
|
CefString text;
|
|
|
|
text.FromWString((std::wstring::value_type*)hGlobal);
|
|
|
|
drag_data->SetFragmentText(text);
|
|
|
|
} else if (format == CF_TEXT) {
|
|
|
|
CefString text;
|
|
|
|
text.FromString((std::string::value_type*)hGlobal);
|
|
|
|
drag_data->SetFragmentText(text);
|
|
|
|
} else if (format == moz_url_format) {
|
|
|
|
std::wstring html((std::wstring::value_type*)hGlobal);
|
|
|
|
size_t pos = html.rfind('\n');
|
|
|
|
CefString url(html.substr(0, pos));
|
|
|
|
CefString title(html.substr(pos + 1));
|
|
|
|
drag_data->SetLinkURL(url);
|
|
|
|
drag_data->SetLinkTitle(title);
|
|
|
|
} else if (format == html_format) {
|
|
|
|
std::string cf_html((std::string::value_type*)hGlobal);
|
|
|
|
std::string base_url;
|
|
|
|
std::string html;
|
|
|
|
CFHtmlToHtml(cf_html, &html, &base_url);
|
|
|
|
drag_data->SetFragmentHtml(html);
|
|
|
|
drag_data->SetFragmentBaseURL(base_url);
|
|
|
|
}
|
|
|
|
if (format == CF_HDROP) {
|
|
|
|
HDROP hdrop = (HDROP)hGlobal;
|
|
|
|
const int kMaxFilenameLen = 4096;
|
|
|
|
const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
|
2015-08-20 16:20:21 +02:00
|
|
|
for (unsigned int x = 0; x < num_files; ++x) {
|
2014-07-01 00:30:29 +02:00
|
|
|
wchar_t filename[kMaxFilenameLen];
|
2015-08-20 16:20:21 +02:00
|
|
|
if (!DragQueryFileW(hdrop, x, filename, kMaxFilenameLen))
|
2014-07-01 00:30:29 +02:00
|
|
|
continue;
|
|
|
|
WCHAR* name = wcsrchr(filename, '\\');
|
|
|
|
drag_data->AddFile(filename, (name ? name + 1 : filename));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (medium.hGlobal)
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
|
|
if (format == CF_HDROP)
|
|
|
|
DragFinish((HDROP)hGlobal);
|
|
|
|
else
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (res == S_OK);
|
|
|
|
enumFormats->Release();
|
|
|
|
return drag_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2015-01-23 18:37:23 +01:00
|
|
|
CComPtr<DropTargetWin> DropTargetWin::Create(OsrDragEvents* callback,
|
|
|
|
HWND hWnd) {
|
2014-07-01 00:30:29 +02:00
|
|
|
return CComPtr<DropTargetWin>(new DropTargetWin(callback, hWnd));
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropTargetWin::DragEnter(IDataObject* data_object,
|
2015-01-23 18:37:23 +01:00
|
|
|
DWORD key_state,
|
|
|
|
POINTL cursor_position,
|
|
|
|
DWORD* effect) {
|
2014-07-01 00:30:29 +02:00
|
|
|
if (!callback_)
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
|
|
|
|
CefRefPtr<CefDragData> drag_data = current_drag_data_;
|
|
|
|
if (!drag_data) {
|
|
|
|
drag_data = DataObjectToDragData(data_object);
|
|
|
|
}
|
|
|
|
CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
|
|
|
|
CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
|
|
|
|
mask = callback_->OnDragEnter(drag_data, ev, mask);
|
|
|
|
*effect = DragOperationToDropEffect(mask);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefBrowserHost::DragOperationsMask DropTargetWin::StartDragging(
|
|
|
|
CefRefPtr<CefBrowser> browser,
|
|
|
|
CefRefPtr<CefDragData> drag_data,
|
|
|
|
CefRenderHandler::DragOperationsMask allowed_ops,
|
2017-05-17 11:29:28 +02:00
|
|
|
int x,
|
|
|
|
int y) {
|
2014-07-01 00:30:29 +02:00
|
|
|
CComPtr<IDataObject> dataObject;
|
|
|
|
DWORD resEffect = DROPEFFECT_NONE;
|
|
|
|
if (DragDataToDataObject(drag_data, &dataObject)) {
|
2017-12-07 22:44:24 +01:00
|
|
|
CComPtr<DropSourceWin> dropSource = DropSourceWin::Create();
|
2014-07-01 00:30:29 +02:00
|
|
|
DWORD effect = DragOperationToDropEffect(allowed_ops);
|
|
|
|
current_drag_data_ = drag_data->Clone();
|
|
|
|
current_drag_data_->ResetFileContents();
|
|
|
|
HRESULT res = DoDragDrop(dataObject, dropSource, effect, &resEffect);
|
|
|
|
if (res != DRAGDROP_S_DROP)
|
|
|
|
resEffect = DROPEFFECT_NONE;
|
2020-01-15 15:28:12 +01:00
|
|
|
current_drag_data_ = nullptr;
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
return DropEffectToDragOperation(resEffect);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropTargetWin::DragOver(DWORD key_state,
|
|
|
|
POINTL cursor_position,
|
|
|
|
DWORD* effect) {
|
|
|
|
if (!callback_)
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
|
|
|
|
CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
|
|
|
|
mask = callback_->OnDragOver(ev, mask);
|
|
|
|
*effect = DragOperationToDropEffect(mask);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropTargetWin::DragLeave() {
|
|
|
|
if (!callback_)
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
callback_->OnDragLeave();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropTargetWin::Drop(IDataObject* data_object,
|
|
|
|
DWORD key_state,
|
|
|
|
POINTL cursor_position,
|
|
|
|
DWORD* effect) {
|
|
|
|
if (!callback_)
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
|
|
|
|
CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
|
|
|
|
mask = callback_->OnDrop(ev, mask);
|
|
|
|
*effect = DragOperationToDropEffect(mask);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
CComPtr<DropSourceWin> DropSourceWin::Create() {
|
|
|
|
return CComPtr<DropSourceWin>(new DropSourceWin());
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropSourceWin::GiveFeedback(DWORD dwEffect) {
|
|
|
|
return DRAGDROP_S_USEDEFAULTCURSORS;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DropSourceWin::QueryContinueDrag(BOOL fEscapePressed,
|
|
|
|
DWORD grfKeyState) {
|
|
|
|
if (fEscapePressed) {
|
|
|
|
return DRAGDROP_S_CANCEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(grfKeyState & MK_LBUTTON)) {
|
|
|
|
return DRAGDROP_S_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
HRESULT DragEnumFormatEtc::CreateEnumFormatEtc(
|
|
|
|
UINT cfmt,
|
2014-07-01 00:30:29 +02:00
|
|
|
FORMATETC* afmt,
|
|
|
|
IEnumFORMATETC** ppEnumFormatEtc) {
|
|
|
|
if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
*ppEnumFormatEtc = new DragEnumFormatEtc(afmt, cfmt);
|
|
|
|
|
|
|
|
return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DragEnumFormatEtc::Next(ULONG celt,
|
|
|
|
FORMATETC* pFormatEtc,
|
|
|
|
ULONG* pceltFetched) {
|
|
|
|
ULONG copied = 0;
|
|
|
|
|
|
|
|
// copy the FORMATETC structures into the caller's buffer
|
|
|
|
while (m_nIndex < m_nNumFormats && copied < celt) {
|
|
|
|
DeepCopyFormatEtc(&pFormatEtc[copied], &m_pFormatEtc[m_nIndex]);
|
|
|
|
copied++;
|
|
|
|
m_nIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// store result
|
|
|
|
if (pceltFetched != 0)
|
|
|
|
*pceltFetched = copied;
|
|
|
|
|
|
|
|
// did we copy all that was requested?
|
|
|
|
return (copied == celt) ? S_OK : S_FALSE;
|
|
|
|
}
|
|
|
|
HRESULT DragEnumFormatEtc::Skip(ULONG celt) {
|
|
|
|
m_nIndex += celt;
|
|
|
|
return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
|
|
|
|
}
|
|
|
|
HRESULT DragEnumFormatEtc::Reset(void) {
|
|
|
|
m_nIndex = 0;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
HRESULT DragEnumFormatEtc::Clone(IEnumFORMATETC** ppEnumFormatEtc) {
|
|
|
|
HRESULT hResult;
|
|
|
|
|
|
|
|
// make a duplicate enumerator
|
|
|
|
hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
|
|
|
|
|
|
|
|
if (hResult == S_OK) {
|
|
|
|
// manually set the index state
|
|
|
|
reinterpret_cast<DragEnumFormatEtc*>(*ppEnumFormatEtc)->m_nIndex = m_nIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
DragEnumFormatEtc::DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats) {
|
|
|
|
AddRef();
|
|
|
|
|
|
|
|
m_nIndex = 0;
|
|
|
|
m_nNumFormats = nNumFormats;
|
|
|
|
m_pFormatEtc = new FORMATETC[nNumFormats];
|
|
|
|
|
|
|
|
// make a new copy of each FORMATETC structure
|
|
|
|
for (int i = 0; i < nNumFormats; i++) {
|
|
|
|
DeepCopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DragEnumFormatEtc::~DragEnumFormatEtc() {
|
|
|
|
// first free any DVTARGETDEVICE structures
|
|
|
|
for (ULONG i = 0; i < m_nNumFormats; i++) {
|
|
|
|
if (m_pFormatEtc[i].ptd)
|
|
|
|
CoTaskMemFree(m_pFormatEtc[i].ptd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now free the main array
|
|
|
|
delete[] m_pFormatEtc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DragEnumFormatEtc::DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) {
|
|
|
|
// copy the source FORMATETC into dest
|
|
|
|
*dest = *source;
|
|
|
|
if (source->ptd) {
|
|
|
|
// allocate memory for the DVTARGETDEVICE if necessary
|
|
|
|
dest->ptd = reinterpret_cast<DVTARGETDEVICE*>(
|
|
|
|
CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
|
|
|
|
|
|
|
|
// copy the contents of the source DVTARGETDEVICE into dest->ptd
|
|
|
|
*(dest->ptd) = *(source->ptd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CComPtr<DataObjectWin> DataObjectWin::Create(FORMATETC* fmtetc,
|
|
|
|
STGMEDIUM* stgmed,
|
|
|
|
int count) {
|
|
|
|
return CComPtr<DataObjectWin>(new DataObjectWin(fmtetc, stgmed, count));
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pmedium) {
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::QueryGetData(FORMATETC* pFormatEtc) {
|
|
|
|
return (LookupFormatEtc(pFormatEtc) == -1) ? DV_E_FORMATETC : S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::GetCanonicalFormatEtc(FORMATETC* pFormatEct,
|
|
|
|
FORMATETC* pFormatEtcOut) {
|
|
|
|
pFormatEtcOut->ptd = NULL;
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::SetData(FORMATETC* pFormatEtc,
|
|
|
|
STGMEDIUM* pMedium,
|
|
|
|
BOOL fRelease) {
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::DAdvise(FORMATETC* pFormatEtc,
|
|
|
|
DWORD advf,
|
|
|
|
IAdviseSink*,
|
|
|
|
DWORD*) {
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::DUnadvise(DWORD dwConnection) {
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
HRESULT DataObjectWin::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise) {
|
2014-07-01 00:30:29 +02:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::EnumFormatEtc(DWORD dwDirection,
|
|
|
|
IEnumFORMATETC** ppEnumFormatEtc) {
|
|
|
|
return DragEnumFormatEtc::CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc,
|
2017-05-17 11:29:28 +02:00
|
|
|
ppEnumFormatEtc);
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DataObjectWin::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) {
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
// try to match the specified FORMATETC with one of our supported formats
|
|
|
|
if ((idx = LookupFormatEtc(pFormatEtc)) == -1)
|
|
|
|
return DV_E_FORMATETC;
|
|
|
|
|
|
|
|
// found a match - transfer data into supplied storage medium
|
|
|
|
pMedium->tymed = m_pFormatEtc[idx].tymed;
|
|
|
|
pMedium->pUnkForRelease = 0;
|
|
|
|
|
|
|
|
// copy the data into the caller's storage medium
|
|
|
|
switch (m_pFormatEtc[idx].tymed) {
|
2017-05-17 11:29:28 +02:00
|
|
|
case TYMED_HGLOBAL:
|
|
|
|
pMedium->hGlobal = DupGlobalMem(m_pStgMedium[idx].hGlobal);
|
|
|
|
break;
|
2014-07-01 00:30:29 +02:00
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
default:
|
|
|
|
return DV_E_FORMATETC;
|
2014-07-01 00:30:29 +02:00
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HGLOBAL DataObjectWin::DupGlobalMem(HGLOBAL hMem) {
|
2017-05-17 11:29:28 +02:00
|
|
|
DWORD len = GlobalSize(hMem);
|
|
|
|
PVOID source = GlobalLock(hMem);
|
|
|
|
PVOID dest = GlobalAlloc(GMEM_FIXED, len);
|
2014-07-01 00:30:29 +02:00
|
|
|
|
|
|
|
memcpy(dest, source, len);
|
|
|
|
GlobalUnlock(hMem);
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DataObjectWin::LookupFormatEtc(FORMATETC* pFormatEtc) {
|
|
|
|
// check each of our formats in turn to see if one matches
|
|
|
|
for (int i = 0; i < m_nNumFormats; i++) {
|
2017-05-17 11:29:28 +02:00
|
|
|
if ((m_pFormatEtc[i].tymed & pFormatEtc->tymed) &&
|
|
|
|
m_pFormatEtc[i].cfFormat == pFormatEtc->cfFormat &&
|
|
|
|
m_pFormatEtc[i].dwAspect == pFormatEtc->dwAspect) {
|
2014-07-01 00:30:29 +02:00
|
|
|
// return index of stored format
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// error, format not found
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataObjectWin::DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count)
|
|
|
|
: ref_count_(0) {
|
|
|
|
m_nNumFormats = count;
|
|
|
|
|
|
|
|
m_pFormatEtc = new FORMATETC[count];
|
|
|
|
m_pStgMedium = new STGMEDIUM[count];
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
m_pFormatEtc[i] = fmtetc[i];
|
|
|
|
m_pStgMedium[i] = stgmed[i];
|
|
|
|
}
|
|
|
|
}
|
2014-10-27 22:34:48 +01:00
|
|
|
|
2015-01-23 18:37:23 +01:00
|
|
|
} // namespace client
|
|
|
|
|
2014-10-27 22:34:48 +01:00
|
|
|
#endif // defined(CEF_USE_ATL)
|