diff options
author | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 18:49:58 +0000 |
---|---|---|
committer | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 18:49:58 +0000 |
commit | 6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1 (patch) | |
tree | 8e833c393312e866250077c15bc1d90464fe99d7 /app | |
parent | 963dfb5a05c5b0e3fa8ed74d803f01cb10fd455e (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | app/os_exchange_data.cc | 4 | ||||
-rw-r--r-- | app/os_exchange_data.h | 48 | ||||
-rw-r--r-- | app/os_exchange_data_provider_win.cc | 253 | ||||
-rw-r--r-- | app/os_exchange_data_provider_win.h | 67 | ||||
-rw-r--r-- | app/os_exchange_data_win_unittest.cc | 2 |
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; |