summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-15 18:49:58 +0000
committerjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-15 18:49:58 +0000
commit6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1 (patch)
tree8e833c393312e866250077c15bc1d90464fe99d7 /app
parent963dfb5a05c5b0e3fa8ed74d803f01cb10fd455e (diff)
downloadchromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.zip
chromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.tar.gz
chromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.tar.bz2
Support dragging a virtual file out of the browser.
BUG=none TEST=none Review URL: http://codereview.chromium.org/351029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36378 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r--app/clipboard/clipboard_util_win.cc2
-rw-r--r--app/os_exchange_data.cc4
-rw-r--r--app/os_exchange_data.h48
-rw-r--r--app/os_exchange_data_provider_win.cc253
-rw-r--r--app/os_exchange_data_provider_win.h67
-rw-r--r--app/os_exchange_data_win_unittest.cc2
6 files changed, 327 insertions, 49 deletions
diff --git a/app/clipboard/clipboard_util_win.cc b/app/clipboard/clipboard_util_win.cc
index 38927bb..6b09074 100644
--- a/app/clipboard/clipboard_util_win.cc
+++ b/app/clipboard/clipboard_util_win.cc
@@ -143,7 +143,7 @@ FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
- static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
diff --git a/app/os_exchange_data.cc b/app/os_exchange_data.cc
index 8f647c2..dc57cd1 100644
--- a/app/os_exchange_data.cc
+++ b/app/os_exchange_data.cc
@@ -128,4 +128,8 @@ bool OSExchangeData::GetFileContents(std::wstring* filename,
bool OSExchangeData::GetHtml(std::wstring* html, GURL* base_url) const {
return provider_->GetHtml(html, base_url);
}
+
+void OSExchangeData::SetDownloadFileInfo(DownloadFileInfo* download) {
+ return provider_->SetDownloadFileInfo(download);
+}
#endif
diff --git a/app/os_exchange_data.h b/app/os_exchange_data.h
index 7efea43..5677409 100644
--- a/app/os_exchange_data.h
+++ b/app/os_exchange_data.h
@@ -18,6 +18,8 @@
#endif
#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
class GURL;
@@ -59,6 +61,48 @@ class OSExchangeData {
#endif
};
+ struct DownloadFileInfo;
+
+ // Defines the interface to observe the status of file download.
+ class DownloadFileObserver : public base::RefCounted<DownloadFileObserver> {
+ public:
+ // The caller is responsible to free the DownloadFileInfo objects passed
+ // in the vector parameter.
+ virtual void OnDataReady(
+ int format,
+ const std::vector<DownloadFileInfo*>& downloads) = 0;
+
+ protected:
+ friend class base::RefCounted<DownloadFileObserver>;
+ virtual ~DownloadFileObserver() {}
+ };
+
+ // Defines the interface to control how a file is downloaded.
+ class DownloadFileProvider :
+ public base::RefCountedThreadSafe<DownloadFileProvider> {
+ public:
+ virtual bool Start(DownloadFileObserver* observer, int format) = 0;
+ virtual void Stop() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<DownloadFileProvider>;
+ virtual ~DownloadFileProvider() {}
+ };
+
+ // Encapsulates the info about a file to be downloaded.
+ struct DownloadFileInfo {
+ FilePath filename;
+ uint64 size;
+ scoped_refptr<DownloadFileProvider> downloader;
+
+ DownloadFileInfo(const FilePath& filename,
+ uint64 size,
+ DownloadFileProvider* downloader)
+ : filename(filename),
+ size(size),
+ downloader(downloader) {}
+ };
+
// Provider defines the platform specific part of OSExchangeData that
// interacts with the native system.
class Provider {
@@ -91,6 +135,7 @@ class OSExchangeData {
virtual bool GetHtml(std::wstring* html, GURL* base_url) const = 0;
virtual bool HasFileContents() const = 0;
virtual bool HasHtml() const = 0;
+ virtual void SetDownloadFileInfo(DownloadFileInfo* download) = 0;
#endif
};
@@ -164,6 +209,9 @@ class OSExchangeData {
bool GetFileContents(std::wstring* filename,
std::string* file_contents) const;
bool GetHtml(std::wstring* html, GURL* base_url) const;
+
+ // Adds a download file with full path (CF_HDROP).
+ void SetDownloadFileInfo(DownloadFileInfo* download);
#endif
private:
diff --git a/app/os_exchange_data_provider_win.cc b/app/os_exchange_data_provider_win.cc
index 9efa5ee..dc28597 100644
--- a/app/os_exchange_data_provider_win.cc
+++ b/app/os_exchange_data_provider_win.cc
@@ -29,6 +29,8 @@ static void GetInternetShortcutFileContents(const GURL& url, std::string* data);
static void CreateValidFileNameFromTitle(const GURL& url,
const std::wstring& title,
std::wstring* validated);
+// Creates a new STGMEDIUM object to hold a file.
+static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path);
// Creates a File Descriptor for the creation of a file to the given URL and
// returns a handle to it.
static STGMEDIUM* GetStorageForFileDescriptor(
@@ -226,12 +228,26 @@ bool OSExchangeDataProviderWin::GetPlainTextURL(IDataObject* source,
}
// static
+DataObjectImpl* OSExchangeDataProviderWin::GetDataObjectImpl(
+ const OSExchangeData& data) {
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
+ data_.get();
+}
+
+// static
IDataObject* OSExchangeDataProviderWin::GetIDataObject(
const OSExchangeData& data) {
- return static_cast<const OSExchangeDataProviderWin&>(data.provider()).
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
data_object();
}
+// static
+IAsyncOperation* OSExchangeDataProviderWin::GetIAsyncOperation(
+ const OSExchangeData& data) {
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
+ async_operation();
+}
+
OSExchangeDataProviderWin::OSExchangeDataProviderWin(IDataObject* source)
: data_(new DataObjectImpl()),
source_object_(source) {
@@ -300,28 +316,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url,
}
void OSExchangeDataProviderWin::SetFilename(const std::wstring& full_path) {
- const size_t drop_size = sizeof(DROPFILES);
- const size_t bytes = drop_size + (full_path.length() + 2) * sizeof(wchar_t);
- HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
- if (!hdata)
- return;
-
- ScopedHGlobal<DROPFILES> locked_mem(hdata);
- DROPFILES* drop_files = locked_mem.get();
- drop_files->pFiles = sizeof(DROPFILES);
- drop_files->fWide = TRUE;
- wchar_t* data = reinterpret_cast<wchar_t*>((BYTE*)(drop_files) + drop_size);
- const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t);
- memcpy(data, full_path.c_str(), copy_size);
- data[full_path.length() + 1] = L'\0'; // Double NULL
-
- // Set up the STGMEDIUM
- STGMEDIUM* storage = new STGMEDIUM;
- storage->tymed = TYMED_HGLOBAL;
- storage->hGlobal = drop_files;
- storage->pUnkForRelease = NULL;
-
- // Set up the StoredDataInfo
+ STGMEDIUM* storage = GetStorageForFileName(full_path);
DataObjectImpl::StoredDataInfo* info =
new DataObjectImpl::StoredDataInfo(CF_HDROP, storage);
data_->contents_.push_back(info);
@@ -347,7 +342,7 @@ void OSExchangeDataProviderWin::SetFileContents(
// Add CFSTR_FILECONTENTS
storage = GetStorageForBytes(file_contents.data(), file_contents.length());
data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
- ClipboardUtil::GetFileContentFormatZero()->cfFormat, storage));
+ ClipboardUtil::GetFileContentFormatZero(), storage));
}
void OSExchangeDataProviderWin::SetHtml(const std::wstring& html,
@@ -459,6 +454,21 @@ bool OSExchangeDataProviderWin::HasCustomFormat(CLIPFORMAT format) const {
return (source_object_->QueryGetData(&format_etc) == S_OK);
}
+void OSExchangeDataProviderWin::SetDownloadFileInfo(
+ OSExchangeData::DownloadFileInfo* download) {
+ // If the filename is not provided, set stoarge to NULL to indicate that
+ // the delay rendering will be used.
+ STGMEDIUM* storage = NULL;
+ if (!download->filename.empty())
+ storage = GetStorageForFileName(download->filename.value());
+
+ // Add CF_HDROP.
+ DataObjectImpl::StoredDataInfo* info = new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetCFHDropFormat()->cfFormat, storage);
+ info->downloads.push_back(download);
+ data_->contents_.push_back(info);
+}
+
///////////////////////////////////////////////////////////////////////////////
// DataObjectImpl, IDataObject implementation:
@@ -533,19 +543,131 @@ static void DuplicateMedium(CLIPFORMAT source_clipformat,
destination->pUnkForRelease->AddRef();
}
-DataObjectImpl::DataObjectImpl() : ref_count_(0) {
- STLDeleteContainerPointers(contents_.begin(), contents_.end());
+DataObjectImpl::DataObjectImpl()
+ : is_aborting_(false),
+ in_async_mode_(false),
+ async_operation_started_(false),
+ observer_(NULL) {
}
DataObjectImpl::~DataObjectImpl() {
+ StopDownloads();
STLDeleteContainerPointers(contents_.begin(), contents_.end());
+ if (observer_)
+ observer_->OnDataObjectDisposed();
+}
+
+void DataObjectImpl::StopDownloads() {
+ for (StoredData::iterator iter = contents_.begin();
+ iter != contents_.end(); ++iter) {
+ for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
+ if ((*iter)->downloads[i]->downloader) {
+ (*iter)->downloads[i]->downloader->Stop();
+ (*iter)->downloads[i]->downloader = 0;
+ }
+ delete (*iter)->downloads[i];
+ }
+ (*iter)->downloads.clear();
+ }
+}
+
+void DataObjectImpl::OnDataReady(
+ int format,
+ const std::vector<OSExchangeData::DownloadFileInfo*>& downloads) {
+ // Find and update the data corresponding to the format.
+ CLIPFORMAT clip_format = static_cast<CLIPFORMAT>(format);
+ DCHECK(clip_format == ClipboardUtil::GetCFHDropFormat()->cfFormat);
+ DataObjectImpl::StoredData::iterator iter = contents_.begin();
+ for (; iter != contents_.end(); ++iter) {
+ if ((*iter)->format_etc.cfFormat == clip_format) {
+ // Update the downloads.
+ DCHECK(downloads.size() == (*iter)->downloads.size());
+ for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
+ OSExchangeData::DownloadFileInfo* old_download = (*iter)->downloads[i];
+ (*iter)->downloads[i] = downloads[i];
+ (*iter)->downloads[i]->downloader = old_download->downloader;
+ delete old_download;
+ }
+
+ // Release the old storage.
+ if ((*iter)->owns_medium) {
+ ReleaseStgMedium((*iter)->medium);
+ delete (*iter)->medium;
+ }
+
+ // Update the storage.
+ (*iter)->owns_medium = true;
+ (*iter)->medium = GetStorageForFileName(downloads[0]->filename.value());
+
+ break;
+ }
+ }
+ DCHECK(iter != contents_.end());
}
HRESULT DataObjectImpl::GetData(FORMATETC* format_etc, STGMEDIUM* medium) {
- StoredData::const_iterator iter = contents_.begin();
+ if (is_aborting_)
+ return DV_E_FORMATETC;
+
+ StoredData::iterator iter = contents_.begin();
while (iter != contents_.end()) {
- if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) {
- DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium);
+ if ((*iter)->format_etc.cfFormat == format_etc->cfFormat &&
+ (*iter)->format_etc.lindex == format_etc->lindex &&
+ ((*iter)->format_etc.tymed & format_etc->tymed)) {
+ // If medium is NULL, delay-rendering will be used.
+ if ((*iter)->medium) {
+ DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium);
+ } else {
+ // Check if the left button is down.
+ bool is_left_button_down = (GetKeyState(VK_LBUTTON) & 0x8000) != 0;
+
+ bool wait_for_data = false;
+ if ((*iter)->in_delay_rendering) {
+ // Make sure the left button is up. Sometimes the drop target, like
+ // Shell, might be too aggresive in calling GetData when the left
+ // button is not released.
+ if (is_left_button_down)
+ return DV_E_FORMATETC;
+
+ wait_for_data = true;
+ } else {
+ // If the left button is up and the target has not requested the data
+ // yet, it probably means that the target does not support delay-
+ // rendering. So instead, we wait for the data.
+ if (is_left_button_down) {
+ (*iter)->in_delay_rendering = true;
+ memset(medium, 0, sizeof(STGMEDIUM));
+ } else {
+ wait_for_data = true;
+ }
+ }
+
+ if (wait_for_data) {
+ // Notify the observer we start waiting for the data. This gives
+ // an observer a chance to end the drag and drop.
+ if (observer_)
+ observer_->OnWaitForData();
+
+ // Now we can start the downloads. Each download will wait till the
+ // necessary data is ready and then return the control.
+ for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
+ if ((*iter)->downloads[i]->downloader) {
+ if (!(*iter)->downloads[i]->downloader->Start(
+ this, format_etc->cfFormat)) {
+ // If any of the download fails to start, abort the whole
+ // process.
+ is_aborting_ = true;
+ StopDownloads();
+ return DV_E_FORMATETC;
+ }
+ }
+ }
+
+ // The stored data should have been updated with the final version.
+ // So we just need to call this function again to retrieve it.
+ return GetData(format_etc, medium);
+ }
+ }
return S_OK;
}
++iter;
@@ -619,13 +741,46 @@ HRESULT DataObjectImpl::EnumDAdvise(IEnumSTATDATA** enumerator) {
}
///////////////////////////////////////////////////////////////////////////////
+// DataObjectImpl, IAsyncOperation implementation:
+
+HRESULT DataObjectImpl::EndOperation(
+ HRESULT result, IBindCtx* reserved, DWORD effects) {
+ async_operation_started_ = false;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::GetAsyncMode(BOOL* is_op_async) {
+ *is_op_async = in_async_mode_ ? TRUE : FALSE;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::InOperation(BOOL* in_async_op) {
+ *in_async_op = async_operation_started_ ? TRUE : FALSE;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::SetAsyncMode(BOOL do_op_async) {
+ in_async_mode_ = (do_op_async == TRUE);
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::StartOperation(IBindCtx* reserved) {
+ async_operation_started_ = true;
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// DataObjectImpl, IUnknown implementation:
HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) {
- *object = NULL;
- if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDataObject)) {
- *object = this;
+ if (!object)
+ return E_POINTER;
+ if (IsEqualIID(iid, IID_IDataObject) || IsEqualIID(iid, IID_IUnknown)) {
+ *object = static_cast<IDataObject*>(this);
+ } else if (in_async_mode_ && IsEqualIID(iid, IID_IAsyncOperation)) {
+ *object = static_cast<IAsyncOperation*>(this);
} else {
+ *object = NULL;
return E_NOINTERFACE;
}
AddRef();
@@ -633,16 +788,13 @@ HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) {
}
ULONG DataObjectImpl::AddRef() {
- return InterlockedIncrement(&ref_count_);
+ base::RefCounted<OSExchangeData::DownloadFileObserver>::AddRef();
+ return 0;
}
ULONG DataObjectImpl::Release() {
- if (InterlockedDecrement(&ref_count_) == 0) {
- ULONG copied_refcnt = ref_count_;
- delete this;
- return copied_refcnt;
- }
- return ref_count_;
+ base::RefCounted<OSExchangeData::DownloadFileObserver>::Release();
+ return 0;
}
///////////////////////////////////////////////////////////////////////////////
@@ -721,6 +873,29 @@ static void CreateValidFileNameFromTitle(const GURL& url,
*validated += extension;
}
+static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path) {
+ const size_t kDropSize = sizeof(DROPFILES);
+ const size_t kTotalBytes =
+ kDropSize + (full_path.length() + 2) * sizeof(wchar_t);
+ HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes);
+
+ ScopedHGlobal<DROPFILES> locked_mem(hdata);
+ DROPFILES* drop_files = locked_mem.get();
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = TRUE;
+ wchar_t* data = reinterpret_cast<wchar_t*>(
+ reinterpret_cast<BYTE*>(drop_files) + kDropSize);
+ const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t);
+ memcpy(data, full_path.c_str(), copy_size);
+ data[full_path.length() + 1] = L'\0'; // Double NULL
+
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->tymed = TYMED_HGLOBAL;
+ storage->hGlobal = drop_files;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
static STGMEDIUM* GetStorageForFileDescriptor(
const std::wstring& valid_file_name) {
DCHECK(!valid_file_name.empty() && valid_file_name.size() + 1 <= MAX_PATH);
diff --git a/app/os_exchange_data_provider_win.h b/app/os_exchange_data_provider_win.h
index 42b11e2..4bd0d80 100644
--- a/app/os_exchange_data_provider_win.h
+++ b/app/os_exchange_data_provider_win.h
@@ -6,15 +6,33 @@
#define APP_OS_EXCHANGE_DATA_PROVIDER_WIN_H_
#include <objidl.h>
+#include <shlobj.h>
#include <string>
#include "app/os_exchange_data.h"
#include "base/scoped_comptr_win.h"
-class DataObjectImpl : public IDataObject {
+class DataObjectImpl : public OSExchangeData::DownloadFileObserver,
+ public IDataObject,
+ public IAsyncOperation {
public:
+ class Observer {
+ public:
+ virtual void OnWaitForData() = 0;
+ virtual void OnDataObjectDisposed() = 0;
+ protected:
+ virtual ~Observer() { }
+ };
+
DataObjectImpl();
- ~DataObjectImpl();
+
+ // Accessors.
+ void set_observer(Observer* observer) { observer_ = observer; }
+
+ // DownloadFileObserver implementation:
+ virtual void OnDataReady(
+ int format,
+ const std::vector<OSExchangeData::DownloadFileInfo*>& downloads);
// IDataObject implementation:
HRESULT __stdcall GetData(FORMATETC* format_etc, STGMEDIUM* medium);
@@ -31,6 +49,14 @@ class DataObjectImpl : public IDataObject {
HRESULT __stdcall DUnadvise(DWORD connection);
HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** enumerator);
+ // IAsyncOperation implementation:
+ HRESULT __stdcall EndOperation(
+ HRESULT result, IBindCtx* reserved, DWORD effects);
+ HRESULT __stdcall GetAsyncMode(BOOL* is_op_async);
+ HRESULT __stdcall InOperation(BOOL* in_async_op);
+ HRESULT __stdcall SetAsyncMode(BOOL do_op_async);
+ HRESULT __stdcall StartOperation(IBindCtx* reserved);
+
// IUnknown implementation:
HRESULT __stdcall QueryInterface(const IID& iid, void** object);
ULONG __stdcall AddRef();
@@ -41,22 +67,34 @@ class DataObjectImpl : public IDataObject {
friend class FormatEtcEnumerator;
friend class OSExchangeDataProviderWin;
+ virtual ~DataObjectImpl();
+
+ void StopDownloads();
+
// Our internal representation of stored data & type info.
struct StoredDataInfo {
FORMATETC format_etc;
STGMEDIUM* medium;
bool owns_medium;
+ bool in_delay_rendering;
+ std::vector<OSExchangeData::DownloadFileInfo*> downloads;
- StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* a_medium) {
+ StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* medium)
+ : medium(medium),
+ owns_medium(true),
+ in_delay_rendering(false) {
format_etc.cfFormat = cf;
format_etc.dwAspect = DVASPECT_CONTENT;
format_etc.lindex = -1;
format_etc.ptd = NULL;
- format_etc.tymed = a_medium->tymed;
-
- owns_medium = true;
+ format_etc.tymed = medium ? medium->tymed : TYMED_HGLOBAL;
+ }
- medium = a_medium;
+ StoredDataInfo(FORMATETC* format_etc, STGMEDIUM* medium)
+ : format_etc(*format_etc),
+ medium(medium),
+ owns_medium(true),
+ in_delay_rendering(false) {
}
~StoredDataInfo() {
@@ -64,6 +102,11 @@ class DataObjectImpl : public IDataObject {
ReleaseStgMedium(medium);
delete medium;
}
+ for (size_t i = 0; i < downloads.size(); ++i) {
+ if (downloads[i]->downloader)
+ downloads[i]->downloader->Stop();
+ }
+ downloads.clear();
}
};
@@ -72,7 +115,10 @@ class DataObjectImpl : public IDataObject {
ScopedComPtr<IDataObject> source_object_;
- LONG ref_count_;
+ bool is_aborting_;
+ bool in_async_mode_;
+ bool async_operation_started_;
+ Observer* observer_;
};
class OSExchangeDataProviderWin : public OSExchangeData::Provider {
@@ -84,7 +130,9 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider {
// that url.
static bool GetPlainTextURL(IDataObject* source, GURL* url);
+ static DataObjectImpl* GetDataObjectImpl(const OSExchangeData& data);
static IDataObject* GetIDataObject(const OSExchangeData& data);
+ static IAsyncOperation* GetIAsyncOperation(const OSExchangeData& data);
explicit OSExchangeDataProviderWin(IDataObject* source);
OSExchangeDataProviderWin();
@@ -92,6 +140,7 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider {
virtual ~OSExchangeDataProviderWin();
IDataObject* data_object() const { return data_.get(); }
+ IAsyncOperation* async_operation() const { return data_.get(); }
// OSExchangeData::Provider methods.
virtual void SetString(const std::wstring& data);
@@ -117,6 +166,8 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider {
virtual bool HasFileContents() const;
virtual bool HasHtml() const;
virtual bool HasCustomFormat(OSExchangeData::CustomFormat format) const;
+ virtual void SetDownloadFileInfo(
+ OSExchangeData::DownloadFileInfo* download_info);
private:
scoped_refptr<DataObjectImpl> data_;
diff --git a/app/os_exchange_data_win_unittest.cc b/app/os_exchange_data_win_unittest.cc
index a237a2f..f4ec8fb 100644
--- a/app/os_exchange_data_win_unittest.cc
+++ b/app/os_exchange_data_win_unittest.cc
@@ -270,7 +270,7 @@ TEST(OSExchangeDataTest, TestURLExchangeFormats) {
CLIPFORMAT cfstr_file_contents =
RegisterClipboardFormat(CFSTR_FILECONTENTS);
FORMATETC format_etc =
- { cfstr_file_contents, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ { cfstr_file_contents, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
STGMEDIUM medium;