1 // Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/osr_dragdrop_win.h"
6
7 #if defined(CEF_USE_ATL)
8
9 #include <shellapi.h>
10 #include <shlobj.h>
11 #include <windowsx.h>
12
13 #include <algorithm>
14 #include <string>
15
16 #include "include/wrapper/cef_helpers.h"
17 #include "tests/cefclient/browser/bytes_write_handler.h"
18 #include "tests/cefclient/browser/resource.h"
19 #include "tests/shared/browser/util_win.h"
20
21 namespace client {
22
23 namespace {
24
DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops)25 DWORD DragOperationToDropEffect(CefRenderHandler::DragOperation allowed_ops) {
26 DWORD effect = DROPEFFECT_NONE;
27 if (allowed_ops & DRAG_OPERATION_COPY)
28 effect |= DROPEFFECT_COPY;
29 if (allowed_ops & DRAG_OPERATION_LINK)
30 effect |= DROPEFFECT_LINK;
31 if (allowed_ops & DRAG_OPERATION_MOVE)
32 effect |= DROPEFFECT_MOVE;
33 return effect;
34 }
35
DropEffectToDragOperation(DWORD effect)36 CefRenderHandler::DragOperationsMask DropEffectToDragOperation(DWORD effect) {
37 DWORD operation = DRAG_OPERATION_NONE;
38 if (effect & DROPEFFECT_COPY)
39 operation |= DRAG_OPERATION_COPY;
40 if (effect & DROPEFFECT_LINK)
41 operation |= DRAG_OPERATION_LINK;
42 if (effect & DROPEFFECT_MOVE)
43 operation |= DRAG_OPERATION_MOVE;
44 return static_cast<CefRenderHandler::DragOperationsMask>(operation);
45 }
46
ToMouseEvent(POINTL p,DWORD key_state,HWND hWnd)47 CefMouseEvent ToMouseEvent(POINTL p, DWORD key_state, HWND hWnd) {
48 CefMouseEvent ev;
49 POINT screen_point = {p.x, p.y};
50 ScreenToClient(hWnd, &screen_point);
51 ev.x = screen_point.x;
52 ev.y = screen_point.y;
53 ev.modifiers = GetCefMouseModifiers(key_state);
54 return ev;
55 }
56
GetStorageForBytes(STGMEDIUM * storage,const void * data,size_t bytes)57 void GetStorageForBytes(STGMEDIUM* storage, const void* data, size_t bytes) {
58 HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
59 if (handle) {
60 memcpy(handle, data, bytes);
61 }
62
63 storage->hGlobal = handle;
64 storage->tymed = TYMED_HGLOBAL;
65 storage->pUnkForRelease = nullptr;
66 }
67
68 template <typename T>
GetStorageForString(STGMEDIUM * stgmed,const std::basic_string<T> & data)69 void GetStorageForString(STGMEDIUM* stgmed, const std::basic_string<T>& data) {
70 GetStorageForBytes(
71 stgmed, data.c_str(),
72 (data.size() + 1) * sizeof(typename std::basic_string<T>::value_type));
73 }
74
GetStorageForFileDescriptor(STGMEDIUM * storage,const std::wstring & file_name)75 void GetStorageForFileDescriptor(STGMEDIUM* storage,
76 const std::wstring& file_name) {
77 DCHECK(!file_name.empty());
78 HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
79
80 FILEGROUPDESCRIPTOR* descriptor =
81 reinterpret_cast<FILEGROUPDESCRIPTOR*>(hdata);
82 descriptor->cItems = 1;
83 descriptor->fgd[0].dwFlags = FD_LINKUI;
84 wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(),
85 std::min(file_name.size(), static_cast<size_t>(MAX_PATH - 1u)));
86
87 storage->tymed = TYMED_HGLOBAL;
88 storage->hGlobal = hdata;
89 storage->pUnkForRelease = nullptr;
90 }
91
92 // Helper method for converting from text/html to MS CF_HTML.
93 // Documentation for the CF_HTML format is available at
94 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
HtmlToCFHtml(const std::string & html,const std::string & base_url)95 std::string HtmlToCFHtml(const std::string& html, const std::string& base_url) {
96 if (html.empty())
97 return std::string();
98
99 #define MAX_DIGITS 10
100 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
101 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
102 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
103
104 static const char* header =
105 "Version:0.9\r\n"
106 "StartHTML:" NUMBER_FORMAT
107 "\r\n"
108 "EndHTML:" NUMBER_FORMAT
109 "\r\n"
110 "StartFragment:" NUMBER_FORMAT
111 "\r\n"
112 "EndFragment:" NUMBER_FORMAT "\r\n";
113 static const char* source_url_prefix = "SourceURL:";
114
115 static const char* start_markup = "<html>\r\n<body>\r\n<!--StartFragment-->";
116 static const char* end_markup = "<!--EndFragment-->\r\n</body>\r\n</html>";
117
118 // Calculate offsets
119 size_t start_html_offset =
120 strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
121 if (!base_url.empty()) {
122 start_html_offset +=
123 strlen(source_url_prefix) + base_url.length() + 2; // Add 2 for \r\n.
124 }
125 size_t start_fragment_offset = start_html_offset + strlen(start_markup);
126 size_t end_fragment_offset = start_fragment_offset + html.length();
127 size_t end_html_offset = end_fragment_offset + strlen(end_markup);
128 char raw_result[1024];
129 _snprintf(raw_result, sizeof(1024), header, start_html_offset,
130 end_html_offset, start_fragment_offset, end_fragment_offset);
131 std::string result = raw_result;
132 if (!base_url.empty()) {
133 result.append(source_url_prefix);
134 result.append(base_url);
135 result.append("\r\n");
136 }
137 result.append(start_markup);
138 result.append(html);
139 result.append(end_markup);
140
141 #undef MAX_DIGITS
142 #undef MAKE_NUMBER_FORMAT_1
143 #undef MAKE_NUMBER_FORMAT_2
144 #undef NUMBER_FORMAT
145
146 return result;
147 }
148
CFHtmlExtractMetadata(const std::string & cf_html,std::string * base_url,size_t * html_start,size_t * fragment_start,size_t * fragment_end)149 void CFHtmlExtractMetadata(const std::string& cf_html,
150 std::string* base_url,
151 size_t* html_start,
152 size_t* fragment_start,
153 size_t* fragment_end) {
154 // Obtain base_url if present.
155 if (base_url) {
156 static std::string src_url_str("SourceURL:");
157 size_t line_start = cf_html.find(src_url_str);
158 if (line_start != std::string::npos) {
159 size_t src_end = cf_html.find("\n", line_start);
160 size_t src_start = line_start + src_url_str.length();
161 if (src_end != std::string::npos && src_start != std::string::npos) {
162 *base_url = cf_html.substr(src_start, src_end - src_start);
163 }
164 }
165 }
166
167 // Find the markup between "<!--StartFragment-->" and "<!--EndFragment-->".
168 // If the comments cannot be found, like copying from OpenOffice Writer,
169 // we simply fall back to using StartFragment/EndFragment bytecount values
170 // to determine the fragment indexes.
171 std::string cf_html_lower = cf_html;
172 size_t markup_start = cf_html_lower.find("<html", 0);
173 if (html_start) {
174 *html_start = markup_start;
175 }
176 size_t tag_start = cf_html.find("<!--StartFragment", markup_start);
177 if (tag_start == std::string::npos) {
178 static std::string start_fragment_str("StartFragment:");
179 size_t start_fragment_start = cf_html.find(start_fragment_str);
180 if (start_fragment_start != std::string::npos) {
181 *fragment_start =
182 static_cast<size_t>(atoi(cf_html.c_str() + start_fragment_start +
183 start_fragment_str.length()));
184 }
185
186 static std::string end_fragment_str("EndFragment:");
187 size_t end_fragment_start = cf_html.find(end_fragment_str);
188 if (end_fragment_start != std::string::npos) {
189 *fragment_end = static_cast<size_t>(atoi(
190 cf_html.c_str() + end_fragment_start + end_fragment_str.length()));
191 }
192 } else {
193 *fragment_start = cf_html.find('>', tag_start) + 1;
194 size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
195 *fragment_end = cf_html.rfind('<', tag_end);
196 }
197 }
198
CFHtmlToHtml(const std::string & cf_html,std::string * html,std::string * base_url)199 void CFHtmlToHtml(const std::string& cf_html,
200 std::string* html,
201 std::string* base_url) {
202 size_t frag_start = std::string::npos;
203 size_t frag_end = std::string::npos;
204
205 CFHtmlExtractMetadata(cf_html, base_url, nullptr, &frag_start, &frag_end);
206
207 if (html && frag_start != std::string::npos &&
208 frag_end != std::string::npos) {
209 *html = cf_html.substr(frag_start, frag_end - frag_start);
210 }
211 }
212
213 const DWORD moz_url_format = ::RegisterClipboardFormat(L"text/x-moz-url");
214 const DWORD html_format = ::RegisterClipboardFormat(L"HTML Format");
215 const DWORD file_desc_format = ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
216 const DWORD file_contents_format =
217 ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
218
DragDataToDataObject(CefRefPtr<CefDragData> drag_data,IDataObject ** data_object)219 bool DragDataToDataObject(CefRefPtr<CefDragData> drag_data,
220 IDataObject** data_object) {
221 const int kMaxDataObjects = 10;
222 FORMATETC fmtetcs[kMaxDataObjects];
223 STGMEDIUM stgmeds[kMaxDataObjects];
224 FORMATETC fmtetc = {0, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
225 int curr_index = 0;
226 CefString text = drag_data->GetFragmentText();
227 if (!text.empty()) {
228 fmtetc.cfFormat = CF_UNICODETEXT;
229 fmtetcs[curr_index] = fmtetc;
230 GetStorageForString(&stgmeds[curr_index], text.ToWString());
231 curr_index++;
232 }
233 if (drag_data->IsLink() && !drag_data->GetLinkURL().empty()) {
234 std::wstring x_moz_url_str = drag_data->GetLinkURL().ToWString();
235 x_moz_url_str += '\n';
236 x_moz_url_str += drag_data->GetLinkTitle().ToWString();
237 fmtetc.cfFormat = moz_url_format;
238 fmtetcs[curr_index] = fmtetc;
239 GetStorageForString(&stgmeds[curr_index], x_moz_url_str);
240 curr_index++;
241 }
242 CefString html = drag_data->GetFragmentHtml();
243 if (!html.empty()) {
244 CefString base_url = drag_data->GetFragmentBaseURL();
245 std::string cfhtml = HtmlToCFHtml(html.ToString(), base_url.ToString());
246 fmtetc.cfFormat = html_format;
247 fmtetcs[curr_index] = fmtetc;
248 GetStorageForString(&stgmeds[curr_index], cfhtml);
249 curr_index++;
250 }
251
252 size_t bufferSize = drag_data->GetFileContents(nullptr);
253 if (bufferSize) {
254 CefRefPtr<BytesWriteHandler> handler = new BytesWriteHandler(bufferSize);
255 CefRefPtr<CefStreamWriter> writer =
256 CefStreamWriter::CreateForHandler(handler.get());
257 drag_data->GetFileContents(writer);
258 DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(bufferSize));
259 CefString fileName = drag_data->GetFileName();
260 GetStorageForFileDescriptor(&stgmeds[curr_index], fileName.ToWString());
261 fmtetc.cfFormat = file_desc_format;
262 fmtetcs[curr_index] = fmtetc;
263 curr_index++;
264 GetStorageForBytes(&stgmeds[curr_index], handler->GetData(),
265 handler->GetDataSize());
266 fmtetc.cfFormat = file_contents_format;
267 fmtetcs[curr_index] = fmtetc;
268 curr_index++;
269 }
270 DCHECK_LT(curr_index, kMaxDataObjects);
271
272 CComPtr<DataObjectWin> obj =
273 DataObjectWin::Create(fmtetcs, stgmeds, curr_index);
274 (*data_object) = obj.Detach();
275 return true;
276 }
277
DataObjectToDragData(IDataObject * data_object)278 CefRefPtr<CefDragData> DataObjectToDragData(IDataObject* data_object) {
279 CefRefPtr<CefDragData> drag_data = CefDragData::Create();
280 IEnumFORMATETC* enumFormats = nullptr;
281 HRESULT res = data_object->EnumFormatEtc(DATADIR_GET, &enumFormats);
282 if (res != S_OK)
283 return drag_data;
284 enumFormats->Reset();
285 const int kCelt = 10;
286
287 ULONG celtFetched;
288 do {
289 celtFetched = kCelt;
290 FORMATETC rgelt[kCelt];
291 res = enumFormats->Next(kCelt, rgelt, &celtFetched);
292 for (unsigned i = 0; i < celtFetched; i++) {
293 CLIPFORMAT format = rgelt[i].cfFormat;
294 if (!(format == CF_UNICODETEXT || format == CF_TEXT ||
295 format == moz_url_format || format == html_format ||
296 format == CF_HDROP) ||
297 rgelt[i].tymed != TYMED_HGLOBAL)
298 continue;
299 STGMEDIUM medium;
300 if (data_object->GetData(&rgelt[i], &medium) == S_OK) {
301 if (!medium.hGlobal) {
302 ReleaseStgMedium(&medium);
303 continue;
304 }
305 void* hGlobal = GlobalLock(medium.hGlobal);
306 if (!hGlobal) {
307 ReleaseStgMedium(&medium);
308 continue;
309 }
310 if (format == CF_UNICODETEXT) {
311 CefString text;
312 text.FromWString((std::wstring::value_type*)hGlobal);
313 drag_data->SetFragmentText(text);
314 } else if (format == CF_TEXT) {
315 CefString text;
316 text.FromString((std::string::value_type*)hGlobal);
317 drag_data->SetFragmentText(text);
318 } else if (format == moz_url_format) {
319 std::wstring html((std::wstring::value_type*)hGlobal);
320 size_t pos = html.rfind('\n');
321 CefString url(html.substr(0, pos));
322 CefString title(html.substr(pos + 1));
323 drag_data->SetLinkURL(url);
324 drag_data->SetLinkTitle(title);
325 } else if (format == html_format) {
326 std::string cf_html((std::string::value_type*)hGlobal);
327 std::string base_url;
328 std::string html;
329 CFHtmlToHtml(cf_html, &html, &base_url);
330 drag_data->SetFragmentHtml(html);
331 drag_data->SetFragmentBaseURL(base_url);
332 }
333 if (format == CF_HDROP) {
334 HDROP hdrop = (HDROP)hGlobal;
335 const int kMaxFilenameLen = 4096;
336 const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
337 for (unsigned int x = 0; x < num_files; ++x) {
338 wchar_t filename[kMaxFilenameLen];
339 if (!DragQueryFileW(hdrop, x, filename, kMaxFilenameLen))
340 continue;
341 WCHAR* name = wcsrchr(filename, '\\');
342 drag_data->AddFile(filename, (name ? name + 1 : filename));
343 }
344 }
345 if (medium.hGlobal)
346 GlobalUnlock(medium.hGlobal);
347 if (format == CF_HDROP)
348 DragFinish((HDROP)hGlobal);
349 else
350 ReleaseStgMedium(&medium);
351 }
352 }
353 } while (res == S_OK);
354 enumFormats->Release();
355 return drag_data;
356 }
357
358 } // namespace
359
Create(OsrDragEvents * callback,HWND hWnd)360 CComPtr<DropTargetWin> DropTargetWin::Create(OsrDragEvents* callback,
361 HWND hWnd) {
362 return CComPtr<DropTargetWin>(new DropTargetWin(callback, hWnd));
363 }
364
DragEnter(IDataObject * data_object,DWORD key_state,POINTL cursor_position,DWORD * effect)365 HRESULT DropTargetWin::DragEnter(IDataObject* data_object,
366 DWORD key_state,
367 POINTL cursor_position,
368 DWORD* effect) {
369 if (!callback_)
370 return E_UNEXPECTED;
371
372 CefRefPtr<CefDragData> drag_data = current_drag_data_;
373 if (!drag_data) {
374 drag_data = DataObjectToDragData(data_object);
375 }
376 CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
377 CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
378 mask = callback_->OnDragEnter(drag_data, ev, mask);
379 *effect = DragOperationToDropEffect(mask);
380 return S_OK;
381 }
382
StartDragging(CefRefPtr<CefBrowser> browser,CefRefPtr<CefDragData> drag_data,CefRenderHandler::DragOperationsMask allowed_ops,int x,int y)383 CefBrowserHost::DragOperationsMask DropTargetWin::StartDragging(
384 CefRefPtr<CefBrowser> browser,
385 CefRefPtr<CefDragData> drag_data,
386 CefRenderHandler::DragOperationsMask allowed_ops,
387 int x,
388 int y) {
389 CComPtr<IDataObject> dataObject;
390 DWORD resEffect = DROPEFFECT_NONE;
391 if (DragDataToDataObject(drag_data, &dataObject)) {
392 CComPtr<DropSourceWin> dropSource = DropSourceWin::Create();
393 DWORD effect = DragOperationToDropEffect(allowed_ops);
394 current_drag_data_ = drag_data->Clone();
395 current_drag_data_->ResetFileContents();
396 HRESULT res = DoDragDrop(dataObject, dropSource, effect, &resEffect);
397 if (res != DRAGDROP_S_DROP)
398 resEffect = DROPEFFECT_NONE;
399 current_drag_data_ = nullptr;
400 }
401 return DropEffectToDragOperation(resEffect);
402 }
403
DragOver(DWORD key_state,POINTL cursor_position,DWORD * effect)404 HRESULT DropTargetWin::DragOver(DWORD key_state,
405 POINTL cursor_position,
406 DWORD* effect) {
407 if (!callback_)
408 return E_UNEXPECTED;
409 CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
410 CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
411 mask = callback_->OnDragOver(ev, mask);
412 *effect = DragOperationToDropEffect(mask);
413 return S_OK;
414 }
415
DragLeave()416 HRESULT DropTargetWin::DragLeave() {
417 if (!callback_)
418 return E_UNEXPECTED;
419 callback_->OnDragLeave();
420 return S_OK;
421 }
422
Drop(IDataObject * data_object,DWORD key_state,POINTL cursor_position,DWORD * effect)423 HRESULT DropTargetWin::Drop(IDataObject* data_object,
424 DWORD key_state,
425 POINTL cursor_position,
426 DWORD* effect) {
427 if (!callback_)
428 return E_UNEXPECTED;
429 CefMouseEvent ev = ToMouseEvent(cursor_position, key_state, hWnd_);
430 CefBrowserHost::DragOperationsMask mask = DropEffectToDragOperation(*effect);
431 mask = callback_->OnDrop(ev, mask);
432 *effect = DragOperationToDropEffect(mask);
433 return S_OK;
434 }
435
Create()436 CComPtr<DropSourceWin> DropSourceWin::Create() {
437 return CComPtr<DropSourceWin>(new DropSourceWin());
438 }
439
GiveFeedback(DWORD dwEffect)440 HRESULT DropSourceWin::GiveFeedback(DWORD dwEffect) {
441 return DRAGDROP_S_USEDEFAULTCURSORS;
442 }
443
QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)444 HRESULT DropSourceWin::QueryContinueDrag(BOOL fEscapePressed,
445 DWORD grfKeyState) {
446 if (fEscapePressed) {
447 return DRAGDROP_S_CANCEL;
448 }
449
450 if (!(grfKeyState & MK_LBUTTON)) {
451 return DRAGDROP_S_DROP;
452 }
453
454 return S_OK;
455 }
456
CreateEnumFormatEtc(UINT cfmt,FORMATETC * afmt,IEnumFORMATETC ** ppEnumFormatEtc)457 HRESULT DragEnumFormatEtc::CreateEnumFormatEtc(
458 UINT cfmt,
459 FORMATETC* afmt,
460 IEnumFORMATETC** ppEnumFormatEtc) {
461 if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
462 return E_INVALIDARG;
463
464 *ppEnumFormatEtc = new DragEnumFormatEtc(afmt, cfmt);
465
466 return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY;
467 }
468
Next(ULONG celt,FORMATETC * pFormatEtc,ULONG * pceltFetched)469 HRESULT DragEnumFormatEtc::Next(ULONG celt,
470 FORMATETC* pFormatEtc,
471 ULONG* pceltFetched) {
472 ULONG copied = 0;
473
474 // copy the FORMATETC structures into the caller's buffer
475 while (m_nIndex < m_nNumFormats && copied < celt) {
476 DeepCopyFormatEtc(&pFormatEtc[copied], &m_pFormatEtc[m_nIndex]);
477 copied++;
478 m_nIndex++;
479 }
480
481 // store result
482 if (pceltFetched != 0)
483 *pceltFetched = copied;
484
485 // did we copy all that was requested?
486 return (copied == celt) ? S_OK : S_FALSE;
487 }
Skip(ULONG celt)488 HRESULT DragEnumFormatEtc::Skip(ULONG celt) {
489 m_nIndex += celt;
490 return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
491 }
Reset(void)492 HRESULT DragEnumFormatEtc::Reset(void) {
493 m_nIndex = 0;
494 return S_OK;
495 }
Clone(IEnumFORMATETC ** ppEnumFormatEtc)496 HRESULT DragEnumFormatEtc::Clone(IEnumFORMATETC** ppEnumFormatEtc) {
497 HRESULT hResult;
498
499 // make a duplicate enumerator
500 hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
501
502 if (hResult == S_OK) {
503 // manually set the index state
504 reinterpret_cast<DragEnumFormatEtc*>(*ppEnumFormatEtc)->m_nIndex = m_nIndex;
505 }
506
507 return hResult;
508 }
509
DragEnumFormatEtc(FORMATETC * pFormatEtc,int nNumFormats)510 DragEnumFormatEtc::DragEnumFormatEtc(FORMATETC* pFormatEtc, int nNumFormats) {
511 AddRef();
512
513 m_nIndex = 0;
514 m_nNumFormats = nNumFormats;
515 m_pFormatEtc = new FORMATETC[nNumFormats];
516
517 // make a new copy of each FORMATETC structure
518 for (int i = 0; i < nNumFormats; i++) {
519 DeepCopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
520 }
521 }
~DragEnumFormatEtc()522 DragEnumFormatEtc::~DragEnumFormatEtc() {
523 // first free any DVTARGETDEVICE structures
524 for (ULONG i = 0; i < m_nNumFormats; i++) {
525 if (m_pFormatEtc[i].ptd)
526 CoTaskMemFree(m_pFormatEtc[i].ptd);
527 }
528
529 // now free the main array
530 delete[] m_pFormatEtc;
531 }
532
DeepCopyFormatEtc(FORMATETC * dest,FORMATETC * source)533 void DragEnumFormatEtc::DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) {
534 // copy the source FORMATETC into dest
535 *dest = *source;
536 if (source->ptd) {
537 // allocate memory for the DVTARGETDEVICE if necessary
538 dest->ptd = reinterpret_cast<DVTARGETDEVICE*>(
539 CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
540
541 // copy the contents of the source DVTARGETDEVICE into dest->ptd
542 *(dest->ptd) = *(source->ptd);
543 }
544 }
545
Create(FORMATETC * fmtetc,STGMEDIUM * stgmed,int count)546 CComPtr<DataObjectWin> DataObjectWin::Create(FORMATETC* fmtetc,
547 STGMEDIUM* stgmed,
548 int count) {
549 return CComPtr<DataObjectWin>(new DataObjectWin(fmtetc, stgmed, count));
550 }
551
GetDataHere(FORMATETC * pFormatEtc,STGMEDIUM * pmedium)552 HRESULT DataObjectWin::GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pmedium) {
553 return E_NOTIMPL;
554 }
555
QueryGetData(FORMATETC * pFormatEtc)556 HRESULT DataObjectWin::QueryGetData(FORMATETC* pFormatEtc) {
557 return (LookupFormatEtc(pFormatEtc) == -1) ? DV_E_FORMATETC : S_OK;
558 }
559
GetCanonicalFormatEtc(FORMATETC * pFormatEct,FORMATETC * pFormatEtcOut)560 HRESULT DataObjectWin::GetCanonicalFormatEtc(FORMATETC* pFormatEct,
561 FORMATETC* pFormatEtcOut) {
562 pFormatEtcOut->ptd = nullptr;
563 return E_NOTIMPL;
564 }
565
SetData(FORMATETC * pFormatEtc,STGMEDIUM * pMedium,BOOL fRelease)566 HRESULT DataObjectWin::SetData(FORMATETC* pFormatEtc,
567 STGMEDIUM* pMedium,
568 BOOL fRelease) {
569 return E_NOTIMPL;
570 }
571
DAdvise(FORMATETC * pFormatEtc,DWORD advf,IAdviseSink *,DWORD *)572 HRESULT DataObjectWin::DAdvise(FORMATETC* pFormatEtc,
573 DWORD advf,
574 IAdviseSink*,
575 DWORD*) {
576 return E_NOTIMPL;
577 }
578
DUnadvise(DWORD dwConnection)579 HRESULT DataObjectWin::DUnadvise(DWORD dwConnection) {
580 return E_NOTIMPL;
581 }
582
EnumDAdvise(IEnumSTATDATA ** ppEnumAdvise)583 HRESULT DataObjectWin::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise) {
584 return E_NOTIMPL;
585 }
586
EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppEnumFormatEtc)587 HRESULT DataObjectWin::EnumFormatEtc(DWORD dwDirection,
588 IEnumFORMATETC** ppEnumFormatEtc) {
589 return DragEnumFormatEtc::CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc,
590 ppEnumFormatEtc);
591 }
592
GetData(FORMATETC * pFormatEtc,STGMEDIUM * pMedium)593 HRESULT DataObjectWin::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) {
594 int idx;
595
596 // try to match the specified FORMATETC with one of our supported formats
597 if ((idx = LookupFormatEtc(pFormatEtc)) == -1)
598 return DV_E_FORMATETC;
599
600 // found a match - transfer data into supplied storage medium
601 pMedium->tymed = m_pFormatEtc[idx].tymed;
602 pMedium->pUnkForRelease = 0;
603
604 // copy the data into the caller's storage medium
605 switch (m_pFormatEtc[idx].tymed) {
606 case TYMED_HGLOBAL:
607 pMedium->hGlobal = DupGlobalMem(m_pStgMedium[idx].hGlobal);
608 break;
609
610 default:
611 return DV_E_FORMATETC;
612 }
613 return S_OK;
614 }
615
DupGlobalMem(HGLOBAL hMem)616 HGLOBAL DataObjectWin::DupGlobalMem(HGLOBAL hMem) {
617 DWORD len = GlobalSize(hMem);
618 PVOID source = GlobalLock(hMem);
619 PVOID dest = GlobalAlloc(GMEM_FIXED, len);
620
621 memcpy(dest, source, len);
622 GlobalUnlock(hMem);
623 return dest;
624 }
625
LookupFormatEtc(FORMATETC * pFormatEtc)626 int DataObjectWin::LookupFormatEtc(FORMATETC* pFormatEtc) {
627 // check each of our formats in turn to see if one matches
628 for (int i = 0; i < m_nNumFormats; i++) {
629 if ((m_pFormatEtc[i].tymed & pFormatEtc->tymed) &&
630 m_pFormatEtc[i].cfFormat == pFormatEtc->cfFormat &&
631 m_pFormatEtc[i].dwAspect == pFormatEtc->dwAspect) {
632 // return index of stored format
633 return i;
634 }
635 }
636
637 // error, format not found
638 return -1;
639 }
640
DataObjectWin(FORMATETC * fmtetc,STGMEDIUM * stgmed,int count)641 DataObjectWin::DataObjectWin(FORMATETC* fmtetc, STGMEDIUM* stgmed, int count)
642 : ref_count_(0) {
643 m_nNumFormats = count;
644
645 m_pFormatEtc = new FORMATETC[count];
646 m_pStgMedium = new STGMEDIUM[count];
647
648 for (int i = 0; i < count; i++) {
649 m_pFormatEtc[i] = fmtetc[i];
650 m_pStgMedium[i] = stgmed[i];
651 }
652 }
653
654 } // namespace client
655
656 #endif // defined(CEF_USE_ATL)
657