• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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